java线程基础

Posted _oldzhang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java线程基础相关的知识,希望对你有一定的参考价值。

1,创建线程的两种方式:

A, 继承Thread类,重写run方法,通过查看Thread源代码可看出run方法只会调用存在的runnable成员变量的target的run方法。

B, 实现runnable接口并传递给Thread类作为成员变量。

package com.thread;

class Thread1 extends Thread
	@Override
	public void run() 
		System.out.println(this.getName());
		super.run();
	

public class ThreadTest1 
	public static void main(String[] args) 
		Thread t = new Thread(new Runnable() 
			@Override
			public void run() 
				System.out.println(Thread.currentThread().getName());
			
		);
		t.start();
		Thread1 t1 = new Thread1();
		t1.start();
	


线程终止的方式:

使用线程的interrupt()方法进行中断,stop()方法进行停止,使用标识位进行取消。

建议使用标识位方法,这种方式使线程在终止时有机会清理资源。

 

Suspend() resume() stop()方法都不建议使用,以suspend()为例,调用后线程不释放资源,比如锁,容易造成死锁,stop也一样。

2,传统计时器:

new Timer().schedule

Timer作为调度器,调用schedule方法执行调度,真正的执行逻辑在TimerTask的实现类中。

3,线程互斥:

同一段逻辑不能同时被多个线程同时执行为线程互斥

volatile和synchronized关键字:

java支持多线程同时访问一个对象或者对象的成员变量,且每个线程可以拥有这个变量的拷贝(虽然对象和成员变量是分配在内存中的共享内存中,但执行的线程还是可以拥有一份拷贝,目的是加速程序的执行)

volatile就是用来修饰成员变量的,保证读取从共享内存读,写入必须刷新到共享内存,保证了成员变量的内存可见性。

Synchronized修饰方法或代码块,保证了线程对变量访问的可见性与排他性。

 

4,线程同步通信:

Wait方法使当前运行的线程等待并释放锁/Notify唤醒其它线程

互斥是指线程不可同时运行,同步通信是指一个线程与其它线程进行协作。

 

等待/通知机制:一个线程A调用了对象O的wait()方法进入等待状态,另一个线程B调用了对象O的notify()方法,线程A收到通知后从对象O的wait()方法返回,继续执行。

A,使用wait(),notify()需要先对调用对象加锁。

B,notify()调用之后,等待线程不会从wait()返回,需要调用notify()的线程释放锁才有机会从wait()中返回

C,等待/通知机制依托于同步机制,其目的就是确保等待线程从wait()方法返回时能够感知到通知线程对变量做出的修改。

 

例子:要求子线程与主线程交替运行,子线程打印10个数,主线程打印20个数,交替运行50次。

分析:

A,打印10个数与打印20个数的方法应该互斥,所以这两个方法都需要加synchronized。保证在子线程打印10个数期间,主线程不能开始打印20个数。

B,子线程打印完后应通知主线程进行打印,需要线程间的通信。需要使用wait与notify

C,设置一个变量来控制此时该哪个线程运行。两个方法为同步关系,应该放在同一类中进行设计。

package com.thread;

class Business 
	private boolean shouldsub = true;
	public synchronized void sub(int i)
		if(!shouldsub)
			try 
				this.wait();
			 catch (InterruptedException e) 
				e.printStackTrace();
			
		
		for(int j = 1; j<=10; j++)
			System.out.println("sub "+j +" in "+i);
		
		shouldsub = false;
		this.notify();
	
	public synchronized void main(int i)
		if(shouldsub)
			try 
				this.wait();
			 catch (InterruptedException e) 
				e.printStackTrace();
			
		
		for(int j = 1; j<=20; j++)
			System.out.println("main "+j +" in "+i);
		
		shouldsub = true;
		this.notify();
	


public class TraditionalThreadcommunication 

	public static void main(String[] args) 
		final Business business = new Business();
		new Thread(new Runnable() 
			@Override
			public void run() 
				for(int i = 1; i<=50; i++)
					business.sub(i);
				 
			
		).start();
		
		for(int i = 1; i<=50; i++)
			business.main(i);
		
	



等待/通知的经典范式:

等待方遵循如下原则:

1,  获取对象的锁

2,  如果条件不满足,则调用对象的wait方法,被通知后仍要检查条件

3,  条件满足则执行对应的逻辑

伪代码如下:

Synchronized(对象)

           While(条件不满足)

         对象.wait();

对应的处理逻辑

通知方遵循如下原则:

1,  获得对象的锁

2,  改变条件

3,  通知所有等待在对象上的线程

对应伪代码如下:

Synchronized(对象)

           改变条件

           对象.notifyAll();

package com.thread;

/**
 * 将共享数据封装在另外一个对象中 然后在Runnable实现类中使用这个对象
 * 每个线程对共享数据的操作方法也分配到这个对象上中 容易实现互斥与通信
 * 
 * 也可以不通过匿名内部类实现 显式声明两个线程实现Runnable
 * 将data1分别作为构造函数参数传入给其中使用也可以
 * @author zhangyaping050
 */
public class MultiThreadShareData 

	public static void main(String[] args) 
		final ShareData1 data1 = new ShareData1();
		new Thread(new Runnable() 
			@Override
			public void run() 
				data1.incre();
			
		).start();
		new Thread(new Runnable() 
			@Override
			public void run() 
				data1.decr();
			
		).start();
	


class ShareData1
	private int j = 0;
	public synchronized void incre()
		j++;
	
	public synchronized void decr()
		j--;
	


5,线程池:

创建固定数目线程池:ExecutorService threadPool = Executors.newFixedThreadPool(3);

创建缓冲线程池:ExecutorService threadPool2 = Executors.newCachedThreadPool();

创建单个线程的线程池:ExecutorService threadPool3 = Executors.newSingleThreadExecutor();

threadPool.execute(new Runnable()

         @Override

         publicvoid run()

                   for(intj=1; j<10; j++)

                       System.out.println(Thread.currentThread().getName()+": "+j+" in "+task);

                  

        

);

往线程池添加执行任务。

调用threadPool.shutdown();线程池执行完任务后会关闭

package com.thread.lock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 简单缓存系统设计 
 * 同一个读写锁对象的多个读锁之间不互斥 读锁与写锁之间互斥 写锁与写锁之间互斥
 * @author zhangyaping050
 */
public class CacheDemo 

	private Map<String, Object> cache = new HashMap<String, Object>();
	private ReadWriteLock rwl = new ReentrantReadWriteLock();

	public static void main(String[] args) 

	

	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 = "aa"; // query db
					
				 finally 
					rwl.writeLock().unlock();
				
				rwl.readLock().lock();
			
		 finally 
			rwl.readLock().unlock();
		
		return value;
	


Condition:

Condition的功能类似传统的wait()和notify()方法的功能

一个锁内部可以有多个Condition,即有多路等待和通知,比wait和notify方法更强大

下面是利用Condition实现阻塞队列

package com.thread.lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * Condition的功能类似传统的wait()和notify()方法的功能
 * 一个锁内部可以有多个Condition,即有多路等待和通知,比wait和notify方法更强大
 * 阻塞队列的实现
 * @author zhangyaping050
 */
public class BlockQueue 

	final Lock lock = new ReentrantLock();
	final Condition notFull = lock.newCondition();
	final Condition notEmpty = lock.newCondition();
	
	final Object[] items = new Object[100];
	int putptr, takeptr, count;

	public void pub(Object x) throws Exception
		lock.lock();
		try
			while(count == items.length)
				notFull.await();
			items[putptr] = x;
			if(++putptr == items.length)
				putptr = 0;
			++count;
			notEmpty.signal();
		finally
			lock.unlock();
		
	
	public Object take() throws Exception
		lock.lock();
		try
			while(count == 0)
				notEmpty.await();
			Object x = items[takeptr];
			if(++takeptr == items.length)
				takeptr = 0;
			--count;
			notFull.signal();
			return x;
		finally
			lock.unlock();
		
	
	public static void main(String[] args) 
		final BlockQueue queue = new BlockQueue();
		new Thread(new Runnable() 
			@Override
			public void run() 
				for(int i=0; i<100; i++)
					try 
						System.out.println("put "+i);
						queue.pub(i);
					 catch (Exception e) 
						e.printStackTrace();
					
				
			
		).start();
		new Thread(new Runnable() 
			@Override
			public void run() 
				for(int i = 0; i<100; i++)
					try 
						System.out.println(queue.take());
					 catch (Exception e) 
						e.printStackTrace();
					
				
			
		).start();
	



以上是关于java线程基础的主要内容,如果未能解决你的问题,请参考以下文章

手把手教你构建源码级组件——Java指定共享线程数目的共享锁

Java并发包中的几种ExecutorService

Java技术专区-问题专区-应该了解的技术点

JAVA多线程提高二:传统线程的互斥与同步&传统线程通信机制

java 基础之--nio 网络编程

java数目