方式一: extends Thread类,覆盖run()方法,调用start()方法执行
public class TestThread {
public static void main(String[] args) {
MyThread myThread=new MyThread();
MyThread myThread=new MyThread("可设置线程名");
myThread.setName("设置线程名");
myThread.start();
}
}
class MyThread extends Thread{
public void run(){
this.getId();获取线程Id
this.getName();获取线程名称
Thread.currentThread();获取当前线程相当于上面this
}
}
方式二:implements Runnable接口,覆盖run()方法,调用start()执行
public class TestThread {
public static void main(String[] args) {
//1创建MyRunnable对象,表示线程要执行的功能
MyRunnable runnable=new MyRunnable();
//2创建线程对象
**Thread thread=new Thread(runnable, "我的线程1");**
//3启动
thread.start();
//也可直接创建可运行对象
Runnable runnable=new Runnable() {
@Override
public void run() {
...
}
};
Thread thread=new Thread(runnable, "我的线程1");
thread.start();
}
}
class MyThread implements Runnable{
public void run(){
...
}
}
一些方法
守护进程(Daemon) 是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
也就是说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”,主线程结束,守护线程也结束。
thread.setDaemon(true);
**插入线程join()**阻塞当前线程直到加入的线程执行完后,当前线程才继续执行,也就是优先执行完刚插入的线程
thread.join();
**设置优先级setPriority(1~10)**值越大优先级越高。
thread.setPriority(10);
设置睡眠时间:Thread.sleep(200);
主动放弃当前线程Thread.yield();
线程安全问题
当多线程并发访问临界资源时,如果破坏原子操作,可能造成数据不一致。
临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证正确性。
原子操作:不可分割的多步操作,被视作一个整体,器顺序和步骤不可打乱或缺省。
线程同步解决线程安全问题
同步代码块
synchronized(临界资源对象){//对临界资源对象加锁
//代码(原子操作)
}
每个对象都有一个互斥锁标记,用来分配给线程的。
只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块。
线程退出同步代码块时,会释放相应的互斥锁标记。
同步方法
synchronized 返回值类型 方法名称(形参列表) {
//代码(原子操作)
}
死锁
例如:当一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
一个线程可以拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁。
线程通信解决死锁
wait();
wait(long);
进入等待队列,同时释放锁 ,和cpu
notify();
notifyAll();
唤醒其他线程
public class BankCard {
//余额
private double money;
//标记
private boolean flag=false;// true 表示有钱可以取钱 false没钱 可以存取
//存钱
public synchronized void save(double m) { //this
while(flag) {//有钱
try {
this.wait();//进入等待队列,同时释放锁 ,和cpu
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
money=money+m;
System.out.println(Thread.currentThread().getName()+"存了"+m+" 余额是"+money);
//修改标记
flag=true;
//唤醒取钱线程
this.notifyAll();
}
//取钱
public synchronized void take(double m) {//this
while(!flag) {
try {
this.wait();//进入等待队列,同时释放锁 ,和cpu
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
money=money-m;
System.out.println(Thread.currentThread().getName()+"取了"+m+" 余额是"+money);
//修改标记
flag=false;
//唤醒存钱线程
this.notifyAll();
}
}
-----------------------------------------------------------------------------
public class BreadCon {
//存放面包的数组
private Bread[] cons=new Bread[6];
//存放面包的位置
private int index=0;
//存放面包
public synchronized void input(Bread b) { //锁this
//判断容器有没有满
while(index>=6) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
cons[index]=b;
System.out.println(Thread.currentThread().getName()+"生产了"+b.getId()+"号面包");
index++;
//唤醒
this.notifyAll();
}
//取出面包
public synchronized void output() {//锁this
while(index<=0) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
index--;
Bread b=cons[index];
System.out.println(Thread.currentThread().getName()+"消费了"+b.getId()+"号面包 生产者:"+b.getProductName());
cons[index]=null;
//唤醒生产者
this.notifyAll();
}
}
线程池
线程容器,可设定线程分配的数量上限。
将预先创建的线程对象存入池中,并重用线程池中的线程对象。
避免频繁的创建和销毁
while(!es.isTerminated()) {//空转
}
如果线程池未结束则空转等待。
/**
* 演示线程池的创建
* Executor:线程池的根接口,execute()
* ExecutorService:包含管理线程池的一些方法,submit shutdown
* ThreadPoolExecutor
* ScheduledThreadPoolExecutor
* Executors:创建线程池的工具类
* (1)创建固定线程个数线程池
* (2)创建缓存线程池,由任务的多少决定
* (3)创建单线程池
* (4)创建调度线程池 调度:周期、定时执行
*/
public static void main(String[] args) {
//1.1创建固定线程个数的线程池
ExecutorService es=Executors.newFixedThreadPool(4);
//1.2创建缓存线程池,线程个数由任务个数决定
**ExecutorService es=Executors.newCachedThreadPool();**
//1.3创建单线程线程池
Executors.newSingleThreadExecutor();
//1.4创建调度线程池 调度:周期、定时执行
//Executors.newScheduledThreadPool(corePoolSize)
**Executors.newScheduledThreadPool(3);**
//2创建任务
Runnable runnable=new Runnable() {
private int ticket=100;
@Override
public void run() {
while(true) {
synchronized (new Object()){
if (ticket <= 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");
ticket--;
}
}
}
};
//3提交任务
for(int i=0;i<5;i++) {
es.submit(runnable);
}
//4关闭线程池
es.shutdown();//等待所有任务执行完毕 然后关闭线程池,不接受新任务。
}
方式三:Callable接口
public interface Callable<V>{
public V call() throws Exception;
}
具有泛型返回值、可以声明异常
/**
* 演示Callable接口的使用
* Callable和Runnable接口的区别
* (1)Callable接口中call方法有返回值,Runnable接口中run方法没有返回值
* (2)Callable接口中call方法有声明异常,Runnable接口中run方法没有异常
* @author wgy
*/
public class Demo2 {
public static void main(String[] args) throws Exception{
//功能需求:使用Callable实现1-100和
//1创建Callable对象
Callable<Integer> callable=new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"开始计算");
int sum=0;
for(int i=1;i<=100;i++) {
sum+=i;
Thread.sleep(100);
}
return sum;
}
};
//2把Callable对象 转成可执行任务
FutureTask<Integer> task=new FutureTask<>(callable);
//3创建线程
Thread thread=new Thread(task);
//4启动线程
thread.start();
//5获取结果(等待call执行完毕,才会返回)
Integer sum=task.get();
System.out.println("结果是:"+sum);
}
}
Future接口
概念:异步接收ExecutorService.submit()所返回的状态结果,当中包含了call()的返回值
get()方法获取结果
/**
* 使用两个线程,并发计算1~50、51~100的和,再进行汇总统计。
* @author wgy
*
*/
public class Demo4 {
public static void main(String[] args) throws Exception{
//1创建线程池
ExecutorService es=Executors.newFixedThreadPool(2);
//2提交任务
Future<Integer> future1=es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum=0;
for(int i=1;i<=50;i++) {
sum+=i;
}
System.out.println("1-50计算完毕");
return sum;
}
});
Future<Integer> future2=es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum=0;
for(int i=51;i<=100;i++) {
sum+=i;
}
System.out.println("51-100计算完毕");
return sum;
}
});
//3获取结果
int sum=future1.get()+future2.get();
System.out.println("结果是:"+sum);
//4关闭线程池
es.shutdown();
}
}
Lock接口
与synchronized比较,显示定义,结构更灵活。
实用性方法,功能更强大,性能更优越。
void lock();获取锁,如锁被占用,则等待。
boolean tryLock();尝试获取锁,不阻塞。
void unlock();释放锁
重入锁ReentrantLock:
//创建锁
private Lock lock=new ReentrantLock();
private String[] str= {"A","B","","",""};
private int count=2;
public void add(String value) {
lock.lock();//开启锁
try {
str[count]=value;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
count++;
System.out.println(Thread.currentThread().getName()+"添加了"+value);
} finally {
lock.unlock();//释放锁
}
}
读写锁ReentrantReadWriteLock
一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁。
支持多次分配读锁,使多个读操作可以并发执行。
互斥规则:
写-写:互斥,阻塞。
读-写:互斥,读阻塞写、写阻塞读。
读-读:不互斥,不阻塞。
public class ReadWriteDemo {
//创建读写锁
private ReentrantReadWriteLock rrl=new ReentrantReadWriteLock();
//获取读锁
private ReadLock readLock=rrl.readLock();
//获取写锁
private WriteLock writeLock=rrl.writeLock();
//互斥锁
private ReentrantLock lock=new ReentrantLock();
private String value;
//读取
public String getValue() {
//使用读锁上锁
lock.lock();
try {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("读取:"+this.value);
return this.value;
}finally {
lock.unlock();
}
}
//写入
public void setValue(String value) {
lock.lock();
try {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("写入:"+value);
this.value=value;
}finally {
lock.unlock();
}
}
}
线程安全的集合
1创建arraylist
ArrayList< String > arrayList=new ArrayList<>();
1.1使用Collections中的线程安全方法转成线程安全的集合
List<String> synList=Collections.synchronizedList(arrayList);
1.2使用CopyOnWriteArrayList
CopyOnWriteArrayList<String> arrayList2=new CopyOnWriteArrayList<>();
1.3使用CopyOnWriteArraySet元素不重复addIf
CopyOnWriteArraySet<String> arrayList3=new CopyOnWriteArraySet<>();
1.4使用ConcurrentHashMap
Map<String,String> map=new ConcurrentHashMap<String,String>();
Queue接口
//1创建队列
Queue<String> queue=new LinkedList<>();
//2入队
queue.offer("苹果");
queue.offer("橘子");
queue.offer("葡萄");
queue.offer("西瓜");
queue.offer("榴莲");
//3出队
System.out.println(queue.peek());//获取第一个元素
System.out.println("----------------");
System.out.println("元素个数:"+queue.size());
int size=queue.size();
for(int i=0;i<size;i++) {
System.out.println(queue.poll());//出队,出首个元素后,后面的元素变首个
}
System.out.println("出队完毕:"+queue.size());
1.ConcurrentLinkedQueue queue=new ConcurrentLinkedQueue<>();
线程安全、可高效读写的队列,高并发下性能最好的队列
2.BlockingQueue接口(阻塞队列)
put();take();
ArrayBlockingQueue: 数组结构实现,有界队列(手工固定上限)
LinkedBlockingQueue: 链表结构实现,无界队列
//创建一个有界队列,添加数据
ArrayBlockingQueue<String> queue=new ArrayBlockingQueue<>(5);
//添加元素
queue.put("aaa");
queue.put("bbb");
queue.put("ccc");
queue.put("ddd");
queue.put("eee");
//删除元素
queue.take();
System.out.println("已经添加了5个元素");
queue.put("xyz");
System.out.println("已经添加了6个元素");
System.out.println(queue.toString());