一、最常見的建立線程的兩種方式
1.繼承Thread
package com.xioayu.threadTest1;
/**
* Created by hasee on 2019/5/26.
*/
public class MyThread1 extends Thread {
public static void main(String[] args) {
MyThread1 t1 = new MyThread1();
MyThread1 t2 = new MyThread1();
t1.setName("線程1");
t2.setName("線程2");
t1.start();
t2.start();
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
【詳解】繼承Thread類,重寫父類的run()方法,然後執行一系列操作。程式首先從mai()方法進入,然後通過new MyThread1()建立了兩個對象,調用setName()可以修改線程名,調用start()方法可以讓線程被開啟。run()方法會被回調,其中currentThread()是靜态方法,sleep()方法也是Thread的靜态方法,代碼裡的sleep()其實就是this.sleep(),而this就是該方法被調用的對象,很顯然是Thread.sleep();
2、實作Runnable接口
package com.xioayu.threadTest1;
/**
* Created by hasee on 2019/5/26.
*/
public class MyThread2 implements Runnable {
public static void main(String[] args) {
MyThread2 t1 = new MyThread2();
MyThread2 t2 = new MyThread2();
Thread thread1=new Thread(t1);
Thread thread2=new Thread(t2);
thread1.setName("線程1");
thread2.setName("線程2");
thread1.start();
thread2.start();
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
【詳解】第二種方式是通過實作Runnable接口,重寫run()方法,而我們還是需要newThread(),調用start()方法開啟線程。這裡的sleep()前面需要加Thread,因為此時的run()是new Thread()這個對象調用的,this指向的是new Thread()這個對象
【運作結果】
二、線程的生命周期
建立了線程之後,調用start()方法,線程進入就緒狀态,隻有當線程獲得了CPU,就會到達運作狀态,此時run()方法就會被執行,可能還沒執行完,失去了CPU,就會回到就緒狀态,等待下次擷取到CPU之後繼續執行run()方法。這就是為什麼上述例子兩個子線程互動執行for循環。通過調用sleep()方法可以讓線程進入阻塞狀态,等待時間到後又回到就緒狀态。
三、給線程加鎖
簡單介紹java的記憶體配置設定,jvm記憶體主要包括
棧:儲存局部變量的值,包括:1.用來儲存基本資料類型的值;2.儲存類的執行個體,即堆區對象的引用(指針)。也可以用來儲存加載方法時的幀。
堆:用來存放動态産生的資料,比如new出來的對象。注意建立出來的對象隻包含屬于各自的成員變量,并不包括成員方法。因為同一個類的對象擁有各自的成員變量,存儲在各自的堆中,但是他們共享該類的方法,并不是每建立一個對象就把成員方法複制一次。
方法區:包含如下内容:
常量池:JVM為每個已加載的類型維護一個常量池,常量池就是這個類型用到的常量的一個有序集合。包括直接常量(基本類型,String)和對其他類型、方法、字段的符号引用(1)。池中的資料和數組一樣通過索引通路。由于常量池包含了一個類型所有的對其他類型、方法、字段的符号引用,是以常量池在Java的動态連結中起了核心作用。常量池存在于方法區中。
代碼段:用來存放從硬碟上讀取的源程式代碼。
資料段:用來存放static定義的靜态成員。
》》其中每個線程都會有一個屬于自己的棧,而堆、方法區都是所有線程共享的,這就存在資料同步的問題。比如在售票的時候,多個視窗去共享同一個資料,這個時候就會出現重票或負票的情況。線程在共享資料方面通過加鎖來實作線程同步,就比如很多人排隊上廁所,然後一個一個的進去,進去一個鎖一下門,等他出來後把鎖交給另一個人。
實作線程同步的方法有兩種:
1.同步代碼塊
2.同步方法
1.實作Runnable的同步代碼塊
package com.xioayu.threadTest1;
/**
* Created by hasee on 2019/5/24.
*/
public class ThreadTest implements Runnable{
/**
* 線程同步:
* 1.同步代碼塊synchronized(同步螢幕:鎖){
* //需要同步的代碼
* }
* 2.同步方法
*
*/
//任何對象都能充當鎖
Object obj=new Object();
public int ticket=100;
@Override
public void run() {
while (true){
synchronized (obj) {//synchronized (this) {
if(ticket>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "賣出第" + ticket + "張票");
ticket--;
}else {
break;
}
}
}
}
public static void main(String[] args) {
ThreadTest t1=new ThreadTest();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
}
}
2.實作Runnable的同步代碼塊
package com.xioayu.threadTest1;
/**
* Created by hasee on 2019/5/24.
*/
public class ThreadTest2 implements Runnable{
/**
* 線程同步:
* 1.同步代碼塊synchronized(同步螢幕:鎖){
* //需要同步的代碼
* }
* 2.同步方法
*
*/
public int ticket=100;
@Override
public void run() {
while (true){
Boolean b=reduce();
if(b){
break;
}
}
}
public synchronized Boolean reduce(){
if(ticket>0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "賣出第" + ticket + "張票");
ticket--;
}else {
return true;
}
return false;
}
public static void main(String[] args) {
ThreadTest2 t1=new ThreadTest2();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
}
}
3.繼承Thread的同步代碼塊
package com.xioayu.threadTest1;
/**
* Created by hasee on 2019/5/24.
*/
public class ThreadTest3 extends Thread {
public static int ticket = 100;
public static void main(String[] args) {
ThreadTest3 t1 = new ThreadTest3();
ThreadTest3 t2 = new ThreadTest3();
ThreadTest3 t3 = new ThreadTest3();
t1.start();
t2.start();
t3.start();
}
@Override
public void run() {
while (true) {
synchronized (ThreadTest3.class) {//synchronized (obj) {
if (ticket > 0) {
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "賣出第" + ticket + "張票");
ticket--;
} else {
break;
}
}
}
}
}
4.繼承Thread的同步代碼塊
package com.xioayu.threadTest1;
/**
* Created by hasee on 2019/5/24.
*/
public class ThreadTest4 extends Thread {
public static int ticket = 100;
public static void main(String[] args) {
ThreadTest4 t1 = new ThreadTest4();
ThreadTest4 t2 = new ThreadTest4();
ThreadTest4 t3 = new ThreadTest4();
t1.start();
t2.start();
t3.start();
}
@Override
public void run() {
while (true) {
Boolean b=ThreadTest4.reduce();
if(b){
break;
}
}
}
public static synchronized boolean reduce() {
if (ticket > 0) {
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "賣出第" + ticket + "張票");
ticket--;
}else {
return true;
}
return false;
}
}
四、讓線程堵塞和喚醒線程
package com.xioayu.threadTest2;
/**
* Created by hasee on 2019/5/24.
*/
public class Number implements Runnable {
public int ticket=100;
@Override
public void run() {
while (true){
synchronized (this) {
notifyAll();
if(ticket>0) {
System.out.println(Thread.currentThread().getName() + "賣出第" + ticket + "張票");
ticket--;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
public static void main(String[] args) {
Number t1=new Number();
new Thread(t1).start();
new Thread(t1).start();
}
}
【結果】
wait():讓線程進入堵塞狀态,并釋放鎖,隻要不喚醒,一直在堵塞狀态。Object類中申明,隻能在同步方法塊内調用
sleep() :讓線程進入堵塞狀态,不釋放鎖,時間結束後自動進入就緒狀态,就是其他線程搶占了CPU也得不到鎖,隻有等該線程再次得到CPU執行結束或者釋放鎖後其他線程開可能得到鎖。Thread類中申明,任何場景都可調用
notify()和notifyAll():喚醒線程,Object類中申明
生成者消費者例子
package com.xioayu.threadTest2;
/**
* Created by hasee on 2019/5/25.
*/
public class ThreadTest5 {
public static void main(String[] args) {
Apple apple=new Apple();
Consumer c1=new Consumer(apple);
Producer p1=new Producer(apple);
Producer p2=new Producer(apple);
Thread t1=new Thread(c1);
Thread t2=new Thread(p1);
Thread t3=new Thread(p2);
t1.setName("生成者1");
t2.setName("消費者1");
t3.setName("消費者2");
t1.start();
t2.start();
t3.start();
}
}
class Consumer implements Runnable{
private Apple apple;
Consumer(Apple apple){
this.apple=apple;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
apple.reduce();
}
}
}
class Producer implements Runnable{
private Apple apple;
Producer(Apple apple){
this.apple=apple;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
apple.add();
}
}
}
class Apple{
private int number=0;
public synchronized void add(){
if(number<5){
number++;
System.out.println("已供應第"+number+"個蘋果");
notifyAll();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void reduce(){
if(number>0){
System.out.println("已消費第"+number+"個蘋果");
number--;
notifyAll();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
五、未來任務
這是JDK5.0之後新增的一種建立線程的方式,讓原本傳回值為void的線程有了傳回值,便于線程之間的通信
package com.xiaoyu.threadTest3;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* Created by hasee on 2019/5/25.
*/
public class FutureTaskTest {
public static void main(String[] args) {
MyCallable myCallable = new MyCallable();
FutureTask<List<User>> listFutureTask = new FutureTask<>(myCallable);
Thread thread = new Thread(listFutureTask);
thread.setName("子線程");
thread.start();
try {
List<User> list=listFutureTask.get();
for(User user:list){
System.out.println(user.getUsername()+"---"+user.getPassword());
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<List<User>>{
@Override
public List<User> call() throws Exception {
List<User> list=new ArrayList<User>();
for (int i=0;i<10;i++) {
Thread.sleep(100);
User user = new User();
user.setUsername(i+"");
user.setPassword(i+"");
list.add(user);
}
return list;
}
}
class User{
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
結果
六、線程池
package com.xioayu.threadTest2;
import java.util.concurrent.*;
/**
* Created by hasee on 2019/5/25.
*/
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
/* ThreadPoolExecutor service1= (ThreadPoolExecutor) service;
service1.setCorePoolSize(5);
service1.setKeepAliveTime();
service1.setMaximumPoolSize(15);*/
service.execute(new MyThread());
Future<Integer> submit = service.submit(new MyThread2());
try {
Integer integer = submit.get();
System.out.println("傳回值"+integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i=i+2){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
class MyThread2 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
for (int i=1;i<100;i=i+2){
System.out.println(Thread.currentThread().getName()+":"+i);
}
return 3;
}
}
execute:實作Runnable接口
submit:實作Callable接口