繼承Thread父類(lèi)
線(xiàn)程代碼執(zhí)行順序和調(diào)用順序無(wú)關(guān)因苹,例如:
public class MyThread extends Thread {
@Override
public void run(){
super.run();
System.out.println("MyThread");
}
/**運(yùn)行順序存疑
* 并沒(méi)有發(fā)現(xiàn)隨機(jī)性 */
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.run();
System.out.println("mainThread");
}
}
上述代碼執(zhí)行理論上“MyThread”和“mainThread”打印順序是隨機(jī)的苟耻,和調(diào)用順序無(wú)關(guān),實(shí)際情況存疑扶檐。
線(xiàn)程執(zhí)行具有隨機(jī)性凶杖,CPU的執(zhí)行具有不確定性
public class MyThread1 extends Thread {
@Override
public void run(){
try {
for (int i = 0;i < 10;i++){
int time = (int) (Math.random()*1000);
Thread.sleep(time);
System.out.println("run:"+Thread.currentThread().getName());
}
}catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
MyThread1 thread = new MyThread1();
thread.setName("myThread");
thread.start();
for (int i = 0; i<10;i++){
int time = (int) (Math.random()*1000);
Thread.sleep(time);
System.out.println("main:"+Thread.currentThread().getName());
}
}
}
/*結(jié)果:
run:myThread
run:myThread
run:myThread
main:main
main:main
run:myThread
run:myThread
run:myThread
main:main
main:main
main:main
main:main
run:myThread
run:myThread
main:main
run:myThread
run:myThread
main:main
main:main
main:main
*/
start方法并不代表線(xiàn)程啟動(dòng),線(xiàn)程啟動(dòng)順序由CPU執(zhí)行順序決定款筑,無(wú)序性智蝠。
public class MyThread2 extends Thread {
private int i;
public MyThread2(int i){
super();
this.i = i;
}
@Override
public void run(){
System.out.println("myThread:"+i);
}
public static void main(String[] args){
MyThread2 t1 = new MyThread2(1);
MyThread2 t2 = new MyThread2(2);
MyThread2 t3 = new MyThread2(3);
MyThread2 t4 = new MyThread2(4);
MyThread2 t5 = new MyThread2(5);
MyThread2 t6 = new MyThread2(6);
MyThread2 t7 = new MyThread2(7);
MyThread2 t8 = new MyThread2(8);
MyThread2 t9 = new MyThread2(9);
MyThread2 t10 = new MyThread2(10);
MyThread2 t11 = new MyThread2(11);
MyThread2 t12 = new MyThread2(12);
MyThread2 t13 = new MyThread2(13);
MyThread2 t14 = new MyThread2(14);
MyThread2 t15 = new MyThread2(15);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
t7.start();
t8.start();
t9.start();
t10.start();
t11.start();
t12.start();
t13.start();
t14.start();
t15.start();
}
}
/*結(jié)果:
myThread:2
myThread:1
myThread:3
myThread:4
myThread:7
myThread:8
myThread:11
myThread:12
myThread:15
myThread:13
myThread:14
myThread:5
myThread:6
myThread:9
myThread:10
*/
Runnable接口構(gòu)造線(xiàn)程
java是單基礎(chǔ)腾么,繼承Thread類(lèi)有局限性,所以更多的是使用Runnable接口去新建線(xiàn)程杈湾,Thread類(lèi)有構(gòu)造方法使用Runnable接口新建線(xiàn)程解虱。
public class RunableTest implements Runnable {
@Override
public void run() {
System.out.println("Runable線(xiàn)程運(yùn)行中:"+Thread.currentThread().getName());
}
public static void main(String[] arg){
RunableTest runableTest = new RunableTest();
Thread thread = new Thread(runableTest);
thread.start();
System.out.println("mainThread:"+Thread.currentThread().getName() );
}
}
實(shí)例變量與線(xiàn)程安全
實(shí)例變量不共享
線(xiàn)程間變量不共享,數(shù)據(jù)不共享情況:
public class ShareThread extends Thread{
private int count = 5;
public ShareThread(String name){
super();
this.setName(name);
}
@Override
public void run(){
super.run();
while (count >0){
count--;
System.out.println("由"+Thread.currentThread().getName()+"計(jì)算,count="+count);
}
}
public static void main(String[] args){
ShareThread shareThread1 = new ShareThread("A");
ShareThread shareThread2 = new ShareThread("B");
ShareThread shareThread3 = new ShareThread("C");
shareThread1.start();
shareThread2.start();
shareThread3.start();
}
}
/*
由A計(jì)算,count=4
由B計(jì)算,count=4
由B計(jì)算,count=3
由B計(jì)算,count=2
由B計(jì)算,count=1
由B計(jì)算,count=0
由A計(jì)算,count=3
由A計(jì)算,count=2
由A計(jì)算,count=1
由A計(jì)算,count=0
由C計(jì)算,count=4
由C計(jì)算,count=3
由C計(jì)算,count=2
由C計(jì)算,count=1
由C計(jì)算,count=0
*/
線(xiàn)程數(shù)據(jù)共享
共享數(shù)據(jù)情況就是多個(gè)線(xiàn)程可以訪問(wèn)同一個(gè)變量。
public class ShareThread1 extends Thread {
private int count = 10;
@Override
synchronized public void run(){
super.run();
count--;
//不要使用for語(yǔ)句漆撞,因?yàn)槭褂猛胶缶€(xiàn)程就沒(méi)有運(yùn)行機(jī)會(huì)了
//一直由線(xiàn)程進(jìn)行減法運(yùn)算
System.out.println("由"+Thread.currentThread().getName()+"計(jì)算殴泰,count="+count);
}
public static void main(String[] args){
ShareThread1 thread1 = new ShareThread1();
Thread a = new Thread(thread1,"A");
Thread b = new Thread(thread1,"B");
Thread c = new Thread(thread1,"C");
Thread d = new Thread(thread1,"D");
Thread e = new Thread(thread1,"E");
Thread f = new Thread(thread1,"F");
a.start();
b.start();
c.start();
d.start();
e.start();
f.start();
}
}
/*結(jié)果:
由A計(jì)算,count=9
由D計(jì)算浮驳,count=8
由E計(jì)算艰匙,count=7
由F計(jì)算,count=6
由C計(jì)算抹恳,count=5
由B計(jì)算员凝,count=4
*/
synchronized關(guān)鍵字表示執(zhí)行多個(gè)線(xiàn)程時(shí)以排隊(duì)的方式進(jìn)行處理。線(xiàn)程執(zhí)行時(shí)會(huì)上鎖奋献,執(zhí)行完畢后會(huì)解鎖健霹,線(xiàn)程調(diào)用run()方法前會(huì)請(qǐng)求線(xiàn)程鎖,若已經(jīng)上鎖瓶蚂,則會(huì)不斷請(qǐng)求線(xiàn)程鎖糖埋。
System.out.println()使用時(shí)可能會(huì)發(fā)生“非線(xiàn)程安全”問(wèn)題,里面打印i--時(shí)窃这,會(huì)先執(zhí)行i--瞳别,然后打印結(jié)果,造成線(xiàn)程安全問(wèn)題。
public class ShareThread2 extends Thread {
private int i = 5;
@Override
public void run(){
System.out.println("i="+ (i--) +",threadName="+Thread.currentThread().getName());
//i--在println之前執(zhí)行杭攻,故可能發(fā)生非線(xiàn)程安全問(wèn)題
}
public static void main(String[] args) {
ShareThread2 run = new ShareThread2();
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
Thread t3 = new Thread(run);
Thread t4 = new Thread(run);
Thread t5 = new Thread(run);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
/*
i=4,threadName=Thread-1
i=5,threadName=Thread-3
i=2,threadName=Thread-5
i=5,threadName=Thread-4
i=3,threadName=Thread-2
*/
常用函數(shù)
currentThread()方法
currentThread返回代碼段被哪個(gè)線(xiàn)程調(diào)用的信息祟敛。
public class CountOpertrate extends Thread {
public CountOpertrate(){
System.out.println("CountOpertate-build-start");
System.out.println("Thread.currentThread().getName() = "+Thread.currentThread().getName());
System.out.println("this.getName()"+this.getName());
System.out.println("CountOpertrate-build-end");
}
@Override
public void run(){
System.out.println("run-start");
System.out.println("Thread.currentThread().getName() = "+Thread.currentThread().getName());
System.out.println("this.getName()"+this.getName());
System.out.println("run-end");
}
public static void main(String[] args) {
CountOpertrate countOpertrate = new CountOpertrate();
Thread t = new Thread(countOpertrate);
t.setName("TEST");
t.start();
}
/*result:
CountOpertate-build-start
Thread.currentThread().getName() = main
this.getName()Thread-0
CountOpertrate-build-end
run-start
Thread.currentThread().getName() = TEST
this.getName()Thread-0
run-end
*/
上述代碼顯示,Count構(gòu)建時(shí)時(shí)用的main線(xiàn)程兆解,run是跑在TEST線(xiàn)程上馆铁。
isAlive()方法
isAlive方法是判斷當(dāng)前線(xiàn)程是處于活動(dòng)狀態(tài)。
public class IsAliveTest extends Thread {
@Override
public void run() {
System.out.println("run = "+this.isAlive());
}
public static void main(String[] args) throws InterruptedException {
IsAliveTest i = new IsAliveTest();
System.out.println("start =="+i.isAlive());
i.start();
Thread.sleep(1000);
System.out.println("end =="+i.isAlive());
}
}
/*result:
start ==false
run = true
end ==false
*/
若將線(xiàn)程對(duì)象以構(gòu)造參數(shù)傳遞給Thread對(duì)象進(jìn)行start锅睛,結(jié)果會(huì)有差異埠巨。
public class IsAliveTest1 extends Thread {
public IsAliveTest1(){
System.out.println("IsAliveTest1-Start");
System.out.println("Thread.currentThread().getName() = "+Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive() = "+Thread.currentThread().isAlive());
System.out.println("this.getName() = "+this.getName());
System.out.println("this.isAlive() = "+this.isAlive());
System.out.println("IsAliveTest1-end");
}
@Override
public void run(){
System.out.println("run-Start");
System.out.println("Thread.currentThread().getName() = "+Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive() = "+Thread.currentThread().isAlive());
System.out.println("this.getName() = "+this.getName());
System.out.println("this.isAlive() = "+this.isAlive());
System.out.println("run-end");
}
public static void main(String[] args) throws InterruptedException {
IsAliveTest1 test1 = new IsAliveTest1();
Thread t1 = new Thread(test1);
System.out.println("main bigin t1 isAlive = "+t1.isAlive());
t1.setName("AAA");
t1.start();
Thread.sleep(1000);
System.out.println("main end t1 isAlive = "+t1.isAlive());
}
}
/*result:
IsAliveTest1-Start
Thread.currentThread().getName() = main
Thread.currentThread().isAlive() = true
this.getName() = Thread-0
this.isAlive() = false
IsAliveTest1-end
main bigin t1 isAlive = false
run-Start
Thread.currentThread().getName() = AAA
Thread.currentThread().isAlive() = true
this.getName() = Thread-0
this.isAlive() = false
run-end
main end t1 isAlive = false
*/
sleep()方法
sleep()方法是在括號(hào)中毫秒內(nèi)使正在執(zhí)行的線(xiàn)程暫停執(zhí)行的方法,正在執(zhí)行的線(xiàn)程是this.currentThread()返回的線(xiàn)程现拒。
public class sleepTest extends Thread {
@Override
public void run() {
try {
System.out.println("run threadName = "+this.getName()+"-begin");
Thread.sleep(2000);
System.out.println("run ThreadName = "+this.getName()+"-end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
sleepTest test = new sleepTest();
System.out.println("begin = "+ System.currentTimeMillis());
test.run();
//test.start();
System.out.println("end = "+System.currentTimeMillis());
}
}
/*直接用run()方法
begin = 1521341785159
run threadName = Thread-0-begin
run ThreadName = Thread-0-end
end = 1521341787160
*/
/*使用start()方法
begin = 1521341902632
end = 1521341902632
run threadName = Thread-0-begin
run ThreadName = Thread-0-end
main和sleepTest線(xiàn)程是異步的辣垒,所以先打印時(shí)間
*/
停止線(xiàn)程
interrupt()方法
interrupt方法并不是立刻停止線(xiàn)程。而是在當(dāng)前線(xiàn)程中打一個(gè)停止標(biāo)記印蔬。
public class InterruptTest extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i<50000;i++){
System.out.println("i = "+ (i+1));
}
}
public static void main(String[] args) {
try {
InterruptTest test = new InterruptTest();
test.start();
Thread.sleep(2000);
Thread.interrupted();
} catch (InterruptedException e) {
System.out.println("main-catch");
e.printStackTrace();
}
}
}
/*
無(wú)法停止勋桶,打印50000條記錄
*/
判斷線(xiàn)程是否是停止?fàn)顟B(tài)
interrupted()方法,測(cè)試當(dāng)前線(xiàn)程是否已經(jīng)是中斷狀態(tài),執(zhí)行后將狀態(tài)標(biāo)志改為false哥遮。
isInterrupted()方法,測(cè)試線(xiàn)程對(duì)象是否已經(jīng)為中斷狀態(tài)陵究,但不清除狀態(tài)標(biāo)志眠饮。
異常法停止線(xiàn)程
可以使用isInterrupted方法判斷線(xiàn)程停止標(biāo)志狀態(tài)并拋出InterruptedException,使用interrupt()方法停止線(xiàn)程后铜邮,因?yàn)榻邮盏酵V範(fàn)顟B(tài)碼仪召,拋出異常進(jìn)入catch分支,繼而終止線(xiàn)程松蒜。
public class StopThreadTest extends Thread {
@Override
public void run() {
super.run();
try {
for (int i=0;i<1000000;i++){
if (this.isInterrupted()){
System.out.println("已是停止?fàn)顟B(tài)扔茅,線(xiàn)程退出!");
throw new InterruptedException();
}
System.out.println("i = "+(i+1));
}
System.out.println("for下面的");
} catch (InterruptedException e) {
System.out.println("線(xiàn)程run()方法catch秸苗!線(xiàn)程異常終止");
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
StopThreadTest test = new StopThreadTest();
test.start();
Thread.sleep(1000);
test.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
/*result:
... ...
i = 274280
i = 274281
i = 274282
i = 274283
i = 274284
end!
已是停止?fàn)顟B(tài)召娜,線(xiàn)程退出玲销!
線(xiàn)程run()方法catch扇丛!線(xiàn)程異常終止
java.lang.InterruptedException
at com.tz.StopThread.StopThreadTest.run(StopThreadTest.java:15)
*/
沉睡中停止進(jìn)程
線(xiàn)程在sleep狀態(tài)下停止榔袋,會(huì)直接報(bào)異常瞳步,并進(jìn)入catch退出蟀给,有兩種情況凸丸,一個(gè)是先sleep再interrupt绅络,還有就是先interrupt再停止挨队。
//先sleep
public class StopSleep1 extends Thread{
@Override
public void run() {
super.run();
try {
System.out.println("run-begin");
Thread.sleep(200000);
System.out.println("run-end");
} catch (InterruptedException e) {
System.out.println("在沉睡中停止弧可,run()進(jìn)入catch "+this.isInterrupted());
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
StopSleep1 sleep1 = new StopSleep1();
sleep1.start();
Thread.sleep(200);
sleep1.interrupt();
} catch (InterruptedException e) {
System.out.println("main-catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
/*result:
run-begin
end!
在沉睡中停止蔑匣,run()進(jìn)入catch false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.tz.StopThread.StopSleep1.run(StopSleep1.java:13)
*/
//后sleep
public class StopSleep2 extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0;i<100000;i++){
System.out.println("i = "+(i+1));
}
System.out.println("run-begin");
Thread.sleep(200000);
System.out.println("run-end");
} catch (InterruptedException e) {
System.out.println("先停止再遇到sleep,run()進(jìn)入catch "+this.isInterrupted());
e.printStackTrace();
}
}
public static void main(String[] args) {
StopSleep2 sleep2 = new StopSleep2();
sleep2.start();
sleep2.interrupt();
System.out.println("end!");
}
}
/*result:
i = 99997
i = 99998
i = 99999
i = 100000
run-begin
先停止再遇到sleep棕诵,run()進(jìn)入catch false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.tz.StopThread.StopSleep2.run(StopSleep2.java:16)
*/
暴力停止線(xiàn)程
使用stop方法停止線(xiàn)程裁良,這個(gè)方法很暴力。
public class StopThread extends Thread {
private int i = 0;
@Override
public void run() {
try {
while (true){
i++;
System.out.println("i=" +i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
StopThread thread = new StopThread();
thread.start();
Thread.sleep(8000);
thread.stop();
System.out.println("stop暴力停止");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
stop方法已經(jīng)作廢校套,盡量不使用E烤谩!搔确!
stop方法釋放鎖彼棍,會(huì)造成數(shù)據(jù)不一致的結(jié)果。
public class StopThread1 extends Thread {
private SynchronizedObject object;
public StopThread1(SynchronizedObject object){
super();
this.object = object;
}
@Override
public void run() {
object.printString("b","bb");
}
public static void main(String[] args) {
try {
SynchronizedObject object = new SynchronizedObject();
StopThread1 thread1 = new StopThread1(object);
thread1.start();
Thread.sleep(500);
thread1.stop();
System.out.println("object.getUsername()="+object.getUsername());
System.out.println("object.getPassword()="+object.getPassword());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*result:
object.getUsername()=b
object.getPassword()=aa
*/
return 停止線(xiàn)程
可以將interrupt()方法與return結(jié)合實(shí)現(xiàn)停止線(xiàn)程膳算。
public class ReturnStopThread extends Thread{
@Override
public void run() {
while(true){
if (this.isInterrupted()){
System.out.println("停止座硕!");
return;
}
System.out.println("timer = "+System.currentTimeMillis());
}
}
public static void main(String[] args) throws InterruptedException {
ReturnStopThread thread = new ReturnStopThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
}
/*result:
... ...
timer = 1521358261861
timer = 1521358261861
timer = 1521358261861
timer = 1521358261861
timer = 1521358261861
timer = 1521358261861
timer = 1521358261861
停止!
*/
建議還是使用拋異常來(lái)停止進(jìn)程涕蜂,因?yàn)閽伄惓华匾?梢酝ㄟ^(guò)catch語(yǔ)句將線(xiàn)程停止事件上拋,是線(xiàn)程停止事件得以傳播。
暫停線(xiàn)程
暫停線(xiàn)程意味著次現(xiàn)場(chǎng)可以恢復(fù)運(yùn)行蜘拉,在Java多線(xiàn)程中可以使用suspend()方法暫停線(xiàn)程萨西,使用resume()方法恢復(fù)線(xiàn)程的執(zhí)行。
public class SuspendTestThread extends Thread {
private long i = 0;
public long getI(){
return i;
}
public void setI(long i) {
this.i = i;
}
@Override
public void run() {
while(true){
i++;
}
}
public static void main(String[] args) {
try {
SuspendTestThread thread = new SuspendTestThread();
thread.start();
Thread.sleep(1000);
//A段
thread.suspend();
System.out.println("線(xiàn)程暫停旭旭!");
System.out.println("A= " +System.currentTimeMillis()+" i="+thread.getI());
Thread.sleep(1000);
System.out.println("A= " +System.currentTimeMillis()+" i="+thread.getI());
//B段
thread.resume();
Thread.sleep(1000);
System.out.println("線(xiàn)程喚醒谎脯!");
System.out.println("B= " +System.currentTimeMillis()+" i="+thread.getI());
Thread.sleep(1000);
System.out.println("B= " +System.currentTimeMillis()+" i="+thread.getI());
//c段
thread.suspend();
System.out.println("線(xiàn)程又暫停");
System.out.println("C= " +System.currentTimeMillis()+" i="+thread.getI());
Thread.sleep(1000);
System.out.println("C= " +System.currentTimeMillis()+" i="+thread.getI());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*result:
線(xiàn)程暫停!
A= 1521516350803 i=620113660
A= 1521516351803 i=620113660
線(xiàn)程喚醒持寄!
B= 1521516352804 i=1259895883
B= 1521516353804 i=1901121177
線(xiàn)程又暫停
C= 1521516353804 i=1901176584
C= 1521516354804 i=1901176584
*/
明顯線(xiàn)程在A和C段暫停執(zhí)行了源梭,在B段喚醒之后又能重新執(zhí)行。
suspend和rusume的缺點(diǎn)
- 獨(dú)占
使用線(xiàn)程暫停時(shí)稍味,如果使用不當(dāng)废麻,容易造成對(duì)公共的同步對(duì)象的獨(dú)占,導(dǎo)致其他線(xiàn)程無(wú)法訪問(wèn)公共同步對(duì)象模庐。
//model.class
public class SynchronizedObject {
synchronized public void printString(){
System.out.println("begin");
if (Thread.currentThread().getName().equals("a")){
System.out.println("a線(xiàn)程永久陷入沉睡烛愧!");
Thread.currentThread().suspend();
}
System.out.println("end");
}
}
public class SuspendTestThread1 extends Thread {
public static void main(String[] args) {
try {
final SynchronizedObject object = new SynchronizedObject();
Thread thread1 = new Thread(){
@Override
public void run() {
object.printString();
}
};
thread1.setName("a");
thread1.start();
Thread.sleep(1000);
Thread thread2 = new Thread(){
@Override
public void run() {
System.out.println("thraed2啟動(dòng),但進(jìn)入不了printString()方法");
System.out.println("因?yàn)閜rintString()方法被a線(xiàn)程鎖定并獨(dú)占了");
object.printString();
}
};
thread2.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*result:
begin
a線(xiàn)程永久陷入沉睡掂碱!
thraed2啟動(dòng)屑彻,但進(jìn)入不了printString()方法
因?yàn)閜rintString()方法被a線(xiàn)程鎖定并獨(dú)占了
*/
- 不同步
因?yàn)榫€(xiàn)程暫停可能會(huì)導(dǎo)致數(shù)據(jù)不同步的情況顶吮。
public class MyObject {
private String username = "l";
private String password = "ll";
public void setValue(String username,String password){
this.username = username;
if (Thread.currentThread().getName().equals("a")){
System.out.println("停止a線(xiàn)程社牲!");
Thread.currentThread().suspend();
}
this.password = password;
}
public void printUsernamePassword(){
System.out.println(username+" "+password);
}
}
public class SuspendTestThread2 extends Thread {
public static void main(String[] args) throws InterruptedException {
final MyObject myObject = new MyObject();
Thread thread1 = new Thread(){
@Override
public void run() {
myObject.setValue("a","aa");
}
};
thread1.setName("a");
thread1.start();
Thread.sleep(500);
Thread thread2 = new Thread(){
@Override
public void run(){
myObject.printUsernamePassword();
}
};
thread2.start();
}
}
/*result:
停止a線(xiàn)程!
a ll
*/
suspend()和resume()方法已經(jīng)廢棄悴了,不建議使用搏恤,可以研究。
yield()方法
yield()方法是讓當(dāng)前線(xiàn)程放棄cpu資源湃交,但放棄的時(shí)間不確定熟空,可能剛剛放棄就立刻獲得cpu資源。
public class YieldTestThread extends Thread {
@Override
public void run() {
long beginTime = System.currentTimeMillis();
int count = 0;
for (int i = 0;i < 50000000; i++){
//Thread.yield();
count = count + (i+1);
}
long endTime = System.currentTimeMillis();
System.out.println("用時(shí):"+(endTime-beginTime)+"毫秒");
}
public static void main(String[] args) {
YieldTestThread thread = new YieldTestThread();
thread.start();
}
}
/*result1:(不加yield)
用時(shí):18毫秒
*/
/*result2:(加yield)
用時(shí):3362毫秒
*/
線(xiàn)程優(yōu)先級(jí)
線(xiàn)程可以劃分優(yōu)先級(jí)搞莺,從1-10級(jí)息罗,其他會(huì)報(bào)錯(cuò)。
線(xiàn)程的優(yōu)先級(jí)具有承繼性才沧。
優(yōu)先級(jí)規(guī)則迈喉,總是大部分先執(zhí)行優(yōu)先級(jí)高的線(xiàn)程。
//線(xiàn)程1
public class PriorityTestThread extends Thread{
@Override
public void run() {
long beginTime = System.currentTimeMillis();
long addResult = 0;
for (int j = 0;j < 10;j++){
for(int i = 0;i<50000;i++){
Random random = new Random();
random.nextInt();
addResult = addResult+1;
}
}
long endTime = System.currentTimeMillis();
System.out.println("* * * * * * thread 1 use time="+(endTime - beginTime));
}
}
//線(xiàn)程2
public class PriorityTestThread1 extends Thread {
@Override
public void run() {
long beginTime = System.currentTimeMillis();
long addResult = 0;
for (int j = 0;j < 10;j++){
for(int i = 0;i<50000;i++){
Random random = new Random();
random.nextInt();
addResult = addResult+1;
}
}
long endTime = System.currentTimeMillis();
System.out.println("* * * * * * thread 2 use time="+(endTime - beginTime));
}
}
public class Run {
public static void main(String[] args) {
for (int i = 0;i < 100;i++){
PriorityTestThread thread1 = new PriorityTestThread();
thread1.setPriority(10);
thread1.start();
PriorityTestThread1 thread2 = new PriorityTestThread1();
thread2.setPriority(1);
thread2.start();
}
}
}
/*result:
... ...
* * * * * * thread 1 use time=6197
* * * * * * thread 1 use time=6207
* * * * * * thread 1 use time=6252
* * * * * * thread 1 use time=6270
* * * * * * thread 2 use time=6870
* * * * * * thread 2 use time=6524
* * * * * * thread 2 use time=7036
* * * * * * thread 1 use time=7522
* * * * * * thread 1 use time=6448
* * * * * * thread 2 use time=7035
* * * * * * thread 2 use time=7223
* * * * * * thread 2 use time=7025
* * * * * * thread 1 use time=7776
* * * * * * thread 1 use time=6747
* * * * * * thread 2 use time=7261
* * * * * * thread 1 use time=7939
... ...
*/
優(yōu)先級(jí)高的不是一定先執(zhí)行温圆。
守護(hù)線(xiàn)程
守護(hù)線(xiàn)程是一種特殊線(xiàn)程挨摸,特性有“陪伴”的含義,當(dāng)進(jìn)程中不存在非守護(hù)進(jìn)程時(shí)岁歉,守護(hù)進(jìn)程就自動(dòng)銷(xiāo)毀了得运。典型的守護(hù)進(jìn)程就是垃圾回收線(xiàn)程(垃圾回收器 GC)
public class DaemonTestThread extends Thread {
private int i = 0;
@Override
public void run() {
try {
while (true){
i++;
System.out.println("i = "+ i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
DaemonTestThread thread = new DaemonTestThread();
thread.setDaemon(true);
thread.start();
Thread.sleep(5000);
System.out.println("我離開(kāi)Thread對(duì)象也不再打印了,也就是停止了!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//線(xiàn)程thread為主線(xiàn)程的守護(hù)進(jìn)程熔掺,主線(xiàn)程停止守護(hù)進(jìn)程也結(jié)束饱搏。
/*result:
i = 1
i = 2
i = 3
i = 4
i = 5
我離開(kāi)Thread對(duì)象也不再打印了,也就是停止了置逻!
*/
對(duì)象及變量的并發(fā)訪問(wèn)
synchronzed同步方法
"非線(xiàn)程安全"會(huì)在多個(gè)線(xiàn)程對(duì)同一個(gè)對(duì)象中的實(shí)例變量進(jìn)行并發(fā)訪問(wèn)時(shí)發(fā)生產(chǎn)生"臟讀",也就是取到的數(shù)據(jù)其實(shí)是被更改過(guò)的推沸。而線(xiàn)程安全就是以獲得的實(shí)例變量的值是經(jīng)過(guò)同步處理的,不會(huì)出現(xiàn)臟讀現(xiàn)象诽偷。
方法內(nèi)數(shù)據(jù)為線(xiàn)程安全
"非線(xiàn)程安全"問(wèn)題存在于"實(shí)例變量"中,如果是方法內(nèi)部私有變量則不存在"非線(xiàn)程安全問(wèn)題"疯坤。
public class HasSelfPrivateNum {
public void addI(String username){
try {
int num = 0;
if (username.equals("a")){
num = 100;
System.out.println("a set over!");
Thread.sleep(2000);
}else {
num = 200;
System.out.println("b set over!");
Thread.sleep(2000);
}
System.out.println(username + " num = "+num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadA extends Thread {
private HasSelfPrivateNum numRef;
public ThreadA(HasSelfPrivateNum numRef){
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("a");
}
}
public class ThreadB extends Thread {
private HasSelfPrivateNum numRef;
public ThreadB(HasSelfPrivateNum numRef){
super();
this.numRef = numRef;
}
@Override
public void run() {
super.run();
numRef.addI("b");
}
}
public class Run {
public static void main(String[] args) {
HasSelfPrivateNum numRef = new HasSelfPrivateNum();
ThreadA threadA = new ThreadA(numRef);
threadA.start();
ThreadB threadB = new ThreadB(numRef);
threadB.start();
}
}
/*result:
a set over!
b set over!
a num = 100
b num = 200
*/
實(shí)例變量非線(xiàn)程安全
若多個(gè)線(xiàn)程訪問(wèn)一個(gè)對(duì)象實(shí)例中的實(shí)例變量报慕。則可能發(fā)生“非線(xiàn)程安全”問(wèn)題。
用線(xiàn)程訪問(wèn)的對(duì)象中如果有多個(gè)實(shí)例變量压怠,則運(yùn)行的結(jié)果有可能出現(xiàn)交叉的情況眠冈。
如果對(duì)象僅有一個(gè)實(shí)例變量,則有可能出現(xiàn)覆蓋的情況菌瘫。
public class HasSelfPrivateNum{
private int num = 0;
//addI()方法前加上synchronized關(guān)鍵字蜗顽,避免“非線(xiàn)程安全問(wèn)題”
synchronized public void addI(String username){
try {
if (username.equals("a")){
num = 100;
System.out.println("a set over!");
Thread.sleep(2000);
}else {
num = 200;
System.out.println("b set over!");
Thread.sleep(2000);
}
System.out.println(username + " num = "+num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadA extends Thread {
private HasSelfPrivateNum numRef;
public ThreadA(HasSelfPrivateNum numRf){
super();
this.numRef = numRf;
}
@Override
public void run() {
super.run();
numRef.addI("a");
}
}
public class ThreadB extends Thread {
private HasSelfPrivateNum numRef;
public ThreadB(HasSelfPrivateNum numRf){
super();
this.numRef = numRf;
}
@Override
public void run() {
super.run();
numRef.addI("b");
}
}
public class Run {
public static void main(String[] args) {
HasSelfPrivateNum numRef = new HasSelfPrivateNum();
ThreadA threadA = new ThreadA(numRef);
threadA.start();
ThreadB threadB = new ThreadB(numRef);
threadB.start();
}
}
/*不加synchronized關(guān)鍵字:
a set over!
b set over!
b num = 200
a num = 200
*/
/*加synchronized關(guān)鍵字:
a set over!
a num = 100
b set over!
b num = 200
*/
多個(gè)對(duì)象多個(gè)鎖
synchronized關(guān)鍵字取得的鎖都對(duì)象鎖,哪個(gè)線(xiàn)程先執(zhí)行帶有synchronized關(guān)鍵字的方法就先獲得對(duì)象鎖雨让,其他線(xiàn)程只能依次等待執(zhí)行完成雇盖。
public class LockTestObject {
synchronized public void methodA(){
try {
System.out.println("Begin methodA threadName = "+Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("methodA end! endTime = "+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void methodB(){
try {
System.out.println("Begin methodB threadName = "+Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("methodB end! endTime = "+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class LockThreadA extends Thread {
private LockTestObject object;
public LockThreadA(LockTestObject object){
super();
this.object = object;
}
@Override
public void run() {
super.run();
object.methodA();
}
}
public class LockThreadB extends Thread {
private LockTestObject object;
public LockThreadB(LockTestObject object){
super();
this.object = object;
}
@Override
public void run() {
super.run();
object.methodB();
}
}
public class Run {
public static void main(String[] args) {
LockTestObject object = new LockTestObject();
LockThreadA threadA = new LockThreadA(object);
threadA.setName("A");
LockThreadB threadB = new LockThreadB(object);
threadB.setName("B");
threadA.start();
threadB.start();
}
}
/*methodA方法不加synchronized關(guān)鍵字
Begin methodB threadName = B
Begin methodA threadName = A
methodB end! endTime = 1521719664578
methodA end! endTime = 1521719664579
*/
/*methodA方法加上synchronized關(guān)鍵字
Begin methodA threadName = A
methodA end! endTime = 1521719556410
Begin methodB threadName = B
methodB end! endTime = 1521719561411
*/
臟讀
所謂臟讀是在對(duì)去實(shí)例變量時(shí)該變量已被其他線(xiàn)程改過(guò),讀出數(shù)據(jù)有誤栖忠。
public class PublicVar {
public String userName = "A";
public String password = "AA";
synchronized public void setValue(String userName,String password){
try {
this.userName = userName;
Thread.sleep(5000);
this.password = password;
System.out.println("setValue method thread name = "+Thread.currentThread().getName()+" userName = "+userName+" password = "+password);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void getValue(){
System.out.println("setValue method thread name = "+Thread.currentThread().getName()+" userName = "+userName+" password = "+password);
}
}
public class DirtyReadTestThread extends Thread{
private PublicVar publicVar;
public DirtyReadTestThread(PublicVar publicVar){
super();
this.publicVar = publicVar;
}
@Override
public void run() {
super.run();
publicVar.setValue("B","BB");
}
}
public class Run {
public static void main(String[] args) {
try {
PublicVar publicVar = new PublicVar();
DirtyReadTestThread thread = new DirtyReadTestThread(publicVar);
thread.start();
Thread.sleep(200);
publicVar.getValue();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*result:
setValue method thread name = main userName = B password = AA
setValue method thread name = Thread-0 userName = B password = BB
*/
如上所示崔挖,main線(xiàn)程出現(xiàn)了臟讀,因?yàn)間etValue()方法不是同步的庵寞,只需在getValue前加上synchronized關(guān)鍵字狸相,即可保持?jǐn)?shù)據(jù)同步性。
synchronized public void getValue(){
System.out.println("setValue method thread name = "+Thread.currentThread().getName()+" userName = "+userName+" password = "+password);
}
/*result:
setValue method thread name = Thread-0 userName = B password = BB
setValue method thread name = main userName = B password = BB
*/
當(dāng)線(xiàn)程調(diào)用對(duì)象包含的synchronized方法時(shí)獲取了對(duì)象的X鎖捐川,但別的線(xiàn)程可以調(diào)用該實(shí)體非synchronized方法脓鹃。
synchronized 鎖重入
一個(gè)線(xiàn)程多次請(qǐng)求synchronized方法鎖時(shí),可以重復(fù)獲得方法所在的對(duì)象實(shí)體的X鎖
鎖重入古沥,即可重復(fù)獲得內(nèi)部鎖瘸右。
public class Service {
synchronized public void service1(){
System.out.println("service1");
service2();
}
synchronized public void service2(){
System.out.println("service2");
service3();
}
synchronized public void service3(){
System.out.println("service3");
}
}
public class LockReentryTestThread extends Thread {
@Override
public void run() {
Service service = new Service();
service.service1();
}
}
public class Run {
public static void main(String[] args) {
LockReentryTestThread thread = new LockReentryTestThread();
thread.start();
}
}
/*result:
service1
service2
service3
*/
service類(lèi)中鎖就重入了,三個(gè)service方法相互調(diào)用岩齿。
鎖重入也支持在父子類(lèi)間的鎖重用尊浓。
出現(xiàn)異常,鎖自動(dòng)釋放纯衍,其他線(xiàn)程繼續(xù)調(diào)用栋齿。
synchronized方法的弊端
導(dǎo)致進(jìn)程等待時(shí)間較長(zhǎng),失去多線(xiàn)程的意義,導(dǎo)致程序響應(yīng)時(shí)間過(guò)長(zhǎng)瓦堵。
synchronized同步塊可以解決這個(gè)問(wèn)題基协。
synchronized同步代碼塊
當(dāng)兩個(gè)并發(fā)的線(xiàn)程訪問(wèn)同一個(gè)對(duì)象object中的synchronized(this)同步塊時(shí),一段時(shí)間內(nèi)只有一個(gè)線(xiàn)程能訪問(wèn)并執(zhí)行菇用,另一個(gè)線(xiàn)程必須等待前一個(gè)線(xiàn)程執(zhí)行完畢這個(gè)代碼塊之后澜驮,才能執(zhí)行這塊代碼。
使用同步synchronized 代碼塊時(shí)惋鸥,同一個(gè)object的同步代碼塊使用同一個(gè)對(duì)象監(jiān)視器杂穷,執(zhí)行一個(gè)同步塊時(shí)對(duì)象中其他同步塊會(huì)被阻塞。
將任意對(duì)象作為對(duì)象監(jiān)視器
鎖非this對(duì)象的優(yōu)點(diǎn)是:若在一個(gè)類(lèi)中有很多個(gè)synchronized方法卦绣,這時(shí)雖然能實(shí)現(xiàn)同步耐量,但會(huì)收到阻塞,影響運(yùn)行效率滤港;若使用同步代碼塊鎖非this對(duì)象廊蜒,則synchronized(非this)代碼塊中的程序與同步方法是異步的,不與其他鎖this的同步方法爭(zhēng)搶this鎖溅漾。則可提高運(yùn)行效率山叮。
靜態(tài)同步synchronized方法與synchronized(class)代碼塊。
關(guān)鍵字還可以作用在static靜態(tài)方法上添履,是對(duì)方法所在的類(lèi).class持鎖屁倔,而不是對(duì)一個(gè)對(duì)象上鎖。
synchronized代碼塊也可以對(duì)class類(lèi)上鎖暮胧,實(shí)現(xiàn)同步汰现。synchronized(xxx.class)。
數(shù)據(jù)類(lèi)型String的常量池特性
由于JVM中String數(shù)據(jù)類(lèi)型的常量池特性 a==b 返回true叔壤,所以不使用String對(duì)象作為對(duì)象監(jiān)視器(對(duì)象鎖)瞎饲。
同步synchronized方法無(wú)限等待與解決
synchronized同步方法容易造成死循環(huán),是形成陷入死鎖炼绘。同步塊可以解開(kāi)這個(gè)死鎖問(wèn)題嗅战,死鎖線(xiàn)程依舊跳不出,但其他線(xiàn)程可獲得鎖俺亮。
多線(xiàn)程的死鎖
synchronized嵌套代碼塊將帶來(lái)死鎖驮捍。
進(jìn)入Cmd 輸入 jsp 查找Run的id值 在輸入 jstack -l 19560 可查看程序運(yùn)行死鎖情況
內(nèi)置類(lèi)與靜態(tài)內(nèi)置類(lèi)
鎖對(duì)象的改變
public class MyService {
private String lock = "123";
public void testMethod() {
try {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis());
lock = "456";
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadA extends Thread {
private MyService service;
public ThreadA(MyService service) {
super();
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
public class ThreadB extends Thread{
private MyService service;
public ThreadB(MyService service) {
super();
this.service = service;
}
@Override
public void run() {
service.testMethod();
}
}
public class Run1 {
public static void main(String[] args) throws InterruptedException {
MyService service = new MyService();
ThreadA threadA = new ThreadA(service);
threadA.setName("A");
ThreadB threadB = new ThreadB(service);
threadB.setName("B");
threadA.start();
//Thread.sleep(50 );
threadB.start();
}
}
/*延遲50毫秒,爭(zhēng)搶兩個(gè)鎖
A begin 1523325785537
B begin 1523325785585
A end 1523325787539
B end 1523325787585
*/
/*不延遲脚曾,爭(zhēng)搶一個(gè)鎖
A begin 1523325815403
A end 1523325817403
B begin 1523325817403
B end 1523325819404
*/
只要鎖對(duì)象不變东且,即使對(duì)象屬性改變依舊同步,線(xiàn)程還是爭(zhēng)搶一個(gè)鎖本讥。