關於線程的概念
創建線程的兩種方式
package com.thread;
public class TestThread {
public static void main(String [] args){
//3、創建一個子類對象
SubThread st=new SubThread();
//4、調用線程的start()方法,開始啓動線程;調用相應的run()方法
st.start();
//這個是主線程的方法
for(int i=1;i<=100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
//1、創建一個繼承Thread的子類
class SubThread extends Thread{
//2、重寫run()方法
@Override
public void run() {
for(int i=1;i<=100;i=i+2){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
package com.thread;
public class ThreadTest {
public static void main(String[] args) throws Exception{
MyRunnable r=new MyRunnable();//c.創建一個Runbable接口實現類的對象的實例
Thread t=new Thread(r);//d.將這個對象作爲形參傳遞給Thread類的構造器中,創建Thread類對象,然後再啓動線程start()方法。
t.setName("自定義線程:");
t.start();
System.out.println(Thread.currentThread().getName());
}
}
class MyRunnable implements Runnable{//a.創建一個類實現Runnable接口
@Override
public void run() {//b.重寫run()方法,將業務邏輯代碼放入在run()方法中
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
3、這兩種方式創建線程的異同在於a.Thread類其實也是實現Runnable接口;b.實現Runnable接口的方式要優於繼承Thread類,首先實現的方式避免java中單繼承的缺點,其次如果多個線程操作同一份數據資源,更加適合用實現的方式
線程中Thread類常用的方法
線程的安全問題
package com.thread;
public class Demo {
public static void main(String[] args) {
sell_Window sell=new sell_Window();
Thread t1=new Thread(sell);//開啓三個窗口同時賣票
Thread t2=new Thread(sell);
Thread t3=new Thread(sell);
t1.start();
t2.start();
t3.start();
}
}
class sell_Window implements Runnable{//實現Runnable接口,把買票的業務邏輯放在run()方法中
int num=100;
@Override
public void run(){
while(true){
synchronized(this) {//利用同步塊來實現線程安全,this表示當前類,我們也可以使用一個對象來做鎖對象,其中利用</span><span style="font-size:12px;">Demo.class字節碼也是比較好的
if(num>0){
try{
Thread.sleep(100);
System.out.println("窗口 "+Thread.currentThread().getName()+":售第"+(num--)+"張票");
}catch (Exception e) {
e.printStackTrace();
}
}else{
break;
}
}
}
}
}
2、同步方法synchronized:將需要同步的方法加上關鍵字:synchronized如publicsynchronized void set(String name){...}
package com.thread;
public class Demo {
public static void main(String[] args) {
sell_Window sell=new sell_Window(new SellTicket());
Thread t1=new Thread(sell);//開啓三個窗口同時賣票
Thread t2=new Thread(sell);
Thread t3=new Thread(sell);
t1.start();
t2.start();
t3.start();
}
}
class SellTicket{//共享數據區,以及買票的方法
int num=100;
public synchronized void sell(){//同步方法
if(num>0){
System.out.println("窗口 "+Thread.currentThread().getName()+":售第"+(num--)+"張票");
}else{
Thread.currentThread().stop();
}
}
}
class sell_Window implements Runnable{//實現Runnable接口,把買票的業務邏輯放在run()方法中
SellTicket sellTicket;
public sell_Window(SellTicket sellTicket) {
this.sellTicket=sellTicket;
}
@Override
public void run(){
while(true){
try{
Thread.sleep(100);
sellTicket.sell();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
package com.thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*線程同步中的鎖Lock */
public class ThreadLock {
public static void main(String[] args) {
new ThreadLock().init();
}
public void init(){
final Outputer outputer=new Outputer();
new Thread(new Runnable() {//匿名線程
@Override
public void run() {//run()方法
while(true){
try {
Thread.sleep(10);
outputer.output("zhangxiaoxiang");//執行的任務
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
Thread.sleep(10);
outputer.output("admin");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
static class Outputer{
Lock lock=new ReentrantLock();//獲取鎖
public void output(String name) {
int len=name.length();
lock.lock();//加鎖
try{
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();//釋放鎖
}
}
}
}
在Lock中存在比較有用的兩種鎖,讀寫鎖(ReadWriteLock),使用這樣的鎖可以保證讀文件的時候可以併發,讀寫和寫文件不可以併發。下面用一個比較實用的例子來說明讀寫鎖的優點。設計一個簡易的cache系統:
package com.thread;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/*設置緩存系統:
* 多個人進行讀取數據時,可以並行,所以這時候只需要讀鎖;當有人發現沒有數據時,這時候需釋放掉
* 讀鎖加上一個寫鎖,在寫的時候不能被讀取,寫完之後就還原成讀鎖讓其它人可以讀取
* */
public class Thread_CacheDemo {
private Map<String,Object> cache=new HashMap<String,Object>();
public static void main(String[] args) {
}
//得到數據
private ReadWriteLock rwl=new ReentrantReadWriteLock();
public Object getData(String key){//多線程讀取時
rwl.readLock().lock();//讀鎖
Object value=null;
try{
value=cache.get(key);
if(value == null){
rwl.readLock().unlock();//釋放讀鎖
rwl.writeLock().lock();//寫鎖
try {
if(value == null){
value="aaaa";//這裏實際上是查詢數據庫
}
} catch (Exception e) {
}finally{
rwl.writeLock().unlock();//釋放寫鎖
rwl.readLock().lock();//加上讀鎖
}
}
}catch (Exception e) {
e.printStackTrace();
}finally{
rwl.readLock().unlock();//釋放讀鎖
}
return value;
}
}
線程間通信
1、線程通信的概念:線程通信是指線程間相互交流的過程,一個線程可以釋放自己的cpu權利來給其它被喚醒的線程使用。
2、線程通信的方法,線程通信主要用到了wait(),notif(),notifyAll(),線程通信的一個經典案例就是生產者和消費者調度算法。下面是生產者和消費者調度算法的實現過程:
package com.thread;
/*生產者與消費者的問題
* 生產者(Productor)將產品交給店員(Clerk),而消費者(Customer)從店員處取走產品
* ,店員一次只能持有固定數量的產品(比如20),如果生產者試圖生 產更多的產品,店員會叫生產者停一下,
* 如果店中有空位放產品了再通知生產者繼續生產;如果店中沒產品了,店員會告訴消費者等一下,如果店中有了產品再
* 通知消費者。
* 1、多線程:生產者,消費者
* 2、共享數據(資源):產品的數量
* 3、需要線程安全
* 4、需要線程通信:生產者與消費者通信
* */
public class Produce_consume {
public static void main(String[] args) {
Clerk clerk=new Clerk();//共享數據
Producer p=new Producer(clerk);//生產者
Consumer c=new Consumer(clerk);//消費者
Thread t1=new Thread(p);//生產者線程
Thread t3=new Thread(p);//生產者線程
Thread t2=new Thread(c);//消費者線程
t1.setName("生產者1");
t2.setName("消費者");
t3.setName("生產者2");
t1.start();
t2.start();
t3.start();
}
}
//生產者
class Producer implements Runnable{
Clerk clerk;
public Producer(Clerk clerk) {
this.clerk=clerk;
}
@Override
public void run() {
System.out.println("生產者開始生產產品");
while(true){
try{
Thread.currentThread().sleep(10);
clerk.addProduct();//生產者生產產品
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消費者
class Consumer implements Runnable{
Clerk clerk;
public Consumer(Clerk clerk){
this.clerk=clerk;
}
@Override
public void run(){
System.out.println("消費者消費產品");
while(true){
try {
Thread.currentThread().sleep(10);
clerk.consumeProduct(); //消費者消費產品
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
//店員,這是一個共享的數據,供生產者和消費者使用
class Clerk{
int product;
public synchronized void addProduct(){
//生產產品
if(product>=20){
try{
//產品數大於或等於20,暫停生產
wait();//釋放cpu資源
} catch (InterruptedException e){
e.printStackTrace();
}
}else{
product++;
System.out.println(Thread.currentThread().getName()+":生產了第"+product+"個產品");
notifyAll();//一旦有產品生產,去喚醒消費者進行消費,notifyAll()喚醒其它線程
}
}
public synchronized void consumeProduct(){
//消費產品
if(product<=0){
try {
wait();//產品數少於或等於0,停止消費
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println(Thread.currentThread().getName()+":消費了第"+product+"個產品");
product--;
notifyAll();//有消費,就去喚醒生產者進行生產
}
}
}
線程範圍的共享數據ThreadLocal類
package com.thread;
import java.util.Random;
public class ThreadLocal_fun {
private static ThreadLocal<Integer> x=new ThreadLocal<Integer>();//表示放置一個變量
private static ThreadLocal<MyThreadScopeDate> xObj=new ThreadLocal<MyThreadScopeDate>();//放置一個變量對象
public static void main(String[] args) {
for(int i=0;i<2;i++){//開啓兩個線程
new Thread(new Runnable() {
@Override
public void run() {
int data=new Random().nextInt();
System.out.println(Thread.currentThread().getName()+" has put data:"+data);
x.set(data);
MyThreadScopeDate.getThreadInstance().setName("admin");//爲每個線程放置變量
MyThreadScopeDate.getThreadInstance().setAge(data);
new A().get();
new B().get();
}
}).start();
}
}
static class A{
public void get(){
int data=x.get();
System.out.println("A:"+Thread.currentThread().getName()+" has put data:"+data);
MyThreadScopeDate obj=MyThreadScopeDate.getThreadInstance();
System.out.println("A"+Thread.currentThread().getName()+":"+obj);
}
}
static class B{
public void get(){
int data=x.get();
System.out.println("B:"+Thread.currentThread().getName()+" has put data:"+data);
MyThreadScopeDate obj=MyThreadScopeDate.getThreadInstance();
System.out.println("A:"+Thread.currentThread().getName()+":"+obj);
}
}
}
//封裝的變量對象 ,單例
class MyThreadScopeDate{
private MyThreadScopeDate(){}//私有化的構造函數不允許實例化
private static ThreadLocal<MyThreadScopeDate> map=new ThreadLocal<MyThreadScopeDate>();
public static MyThreadScopeDate getThreadInstance(){
MyThreadScopeDate instance=map.get();
if(instance == null){
instance=new MyThreadScopeDate();
map.set(instance);
}
return instance;
}
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "MyThreadScopeDate [name=" + name + ", age=" + age + "]";
}
}
線程池
package com.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args) {
//ExecutorService threadPool=Executors.newFixedThreadPool(3);//創建固定大小的線程池,這裏表明只能同時運行3個線程。
//ExecutorService threadPool=Executors.newCachedThreadPool();//創建緩存線程池,爲所有的任務都分配一個線程,自動關閉空閒的線程
ExecutorService threadPool=Executors.newSingleThreadExecutor();//創建單一線程池。
for(int i=1;i<10;i++){//循環往線程池中放10個任務
final int task=i;
threadPool.execute(new Runnable() {//在線程池中放任務
@Override
public void run() {
for(int j=5;j<10;j++){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程"+Thread.currentThread().getName()+" loop of"+j+"次,在執行第 "+task+"個任務!");
}
}
});
}
System.out.println("提交了10個任務");
//threadPool.shutdown();//當所有的任務都運行完後,就關閉線程池
//threadPool.shutdownNow();//立刻關閉線程池
}
}