目录
1、进程与线程
1、进程
操作系统中的最小单位。通过进程可以创建程序。
2、线程
线程是进程中的最小单位,一个进程中可以包含多个线程,并且可以同时执行,从而形成了多线程状态。
2、线程的五种状态
新建-->就绪-->运行-->阻塞-->死亡
3、自定义线程实现方式
1、继承Thread类
//1、创建一个继承Thread类的子类
public class TestThread extends Thread{
//2、重写run方法
@Override
public void run() {
for(int i = 0 ; i < 50 ; i++){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class Test {
public static void main(String[] args) {
//3、创建Thread类的子类的对象
TestThread thread = new TestThread();
//4、调用strat()方法,从而调用线程的run方法,不能直接调用run方法,因为
//这样只是简单的方法调用,并未创建线程
//5、启动线程
thread.start();
}
}
优点:继承方式,优点明显,编程方式比较简单,直接就可以调用
缺点:单继承,扩展时困难
2、实现Runnable接口
//创建一个实现了Runnable接口的类
public class TestRunnable implements Runnable{
//实现接口中的run方法
@Override
public void run() {
for(int i = 0 ; i < 50 ; i++){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class Test {
public static void main(String[] args) {
//创建实现类的对象
TestRunnable testRunnable = new TestRunnable();
//将对象作为参数传递到Thread类的构造器中,创建Thread类的对象,并对其命名
Thread t1 = new Thread(testRunnable,"线程1");
//通过Thread类的对象调用start(),从而调用当前线程的run()
t1.start();
Thread t2 = new Thread(testRunnable,"线程2");
t2.start();
}
}
优点:先将线程类进行实例化,再利用Thread实现线程的 调用,稍显复杂。 更高灵活度,可以实现继承的同时,还可以实现多接口。 可以实现任务共享机制。
1、run方法调用和start调用的区别
run方法是定义在线程类中,作为自定义线程,需要重写run方 法,如果直接通过main主线程调用,就和调用普通的对象方法是一 样的,并不会让线程单独启动。而start方法是定义在Thread类中 的,通过Start方法,jvm虚拟机会自动的调用run方法,执行线程任 务,所以该种方式是能够实现多线程的方式。一个线程不能够重复 执行。
3、实现Callable接口
//创建一个实现Callable的实现类
public class TestCallable implements Callable {
//实现call方法,此线程需要在call()中执行
@Override
public Object call() throws Exception {
int sum = 0;
//把50以内的相加
for (int i = 1; i <= 50; i++) {
System.out.println(i);
sum += i;
}
return sum;
}
}
public class Test {
public static void main(String[] args) {
//创建Callable接口实现类的对象
TestCallable testCallable = new TestCallable();
//将此接口实现类的对象传递到FutureTask中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(testCallable);
//将FutureTask的对象传递到Thread类中,创建Thread对象
new Thread(futureTask).start();
try{
//获取Callable中call方法的返回值,get()即为此方法
Object sum = futureTask.get();
System.out.println(sum);
}catch (InterruptedException e){
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
优点:call()可以有返回值的,可以抛出异常,被外面的操作捕获,获取异常的信息。Callable是支持泛型的
4、使用线程池
public class TestExcutors implements Runnable{
@Override
public void run() {
for(int i = 0 ; i < 50 ; i++){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class TestExcutors1 implements Runnable
{
@Override
public void run() {
for(int i = 0 ; i < 50 ; i++){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
public class Test1 {
public static void main(String[] args) {
//提供指定线程数量的线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
//输出class java.util.concurrent.ThreadPoolExecutor
System.out.println(executorService.getClass());
ThreadPoolExecutor service1 = (ThreadPoolExecutor) executorService;
//执行指定的线程操作,需要提供实现Runnable接口或Callable接口实现类的对象
service1.execute(new TestExcutors());//适用于Runnable
service1.execute(new TestExcutors1());
//service1.submit(Callable callable);//适用于Callable
service1.shutdown();
}
}
优点:1.提高响应速度(减少了创建新线程的时间)2.降低资源消耗(重复利用线程池中线程)3.便于线程管理
5、使用匿名类
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 线程需要执行的任务代码
System.out.println("子线程开始启动....");
for (int i = 0; i < 50; i++) {
System.out.println("run i:" + i);
}
}
});
thread.start();
4、线程调度
1、setPriority
更改线程的优先级,优先级是从1~10设置,默认是5.
Thread thread1 = new Thread(tr1,"线程优先级
高") ;
//设置线程的优先级
thread1.setPriority(10);
System.out.println(thread1.getPriority());
thread1.start();
2、sleep
能够让当前线程进入休眠状态,通过毫秒的参数进行设定。
public class SleepTest extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "=" + i);
}
}
public static void main(String[] args) {
SleepTest st = new SleepTest() ;
//调用
st.start();
SleepTest st1 = new SleepTest() ;
//调用
st1.start();
SleepTest st2 = new SleepTest() ;
//调用
st2.start();
SleepTest st3 = new SleepTest() ;
//调用
st3.start();
}
}
3、join
让一条线程强制插入到正在运行的线程过程中,在执行后,再让出执行权。
public void sleep(){
while (true) {
//每秒睡一次
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Date date = new Date();
System.out.println(date);
}
}
//main方法:
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "=" + i);
if(i == 5) {
try {
st.join(); //强势性,插入执行后,其他线程全部等待
} catch (InterruptedException e){
throw newRuntimeException(e);
}
}
}
4、yield
线程礼让,暂停当前正在执行的线程对象,让其他线程运行, 但是不保证一定会礼让。
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "=" + i);
if(i == 5) {
Thread.yield();
}
}
5、等待和唤醒
拿包子铺和包子的案例进行说明
//此代码写的是死循环,需自己点击暂停
package com;
import java.util.List;
public class BunStore extends Thread{
private List<String> list;
public BunStore(List<String> list){
this.list = list;
}
@Override
public void run() {
while(true){
synchronized (list){
if(list.size() > 0){
try {
//存元素的线程进入到等待状态
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果线程没进入到等待状态 说明集合中没有元素
//向集合中添加元素
list.add("生产了一个包子。。。。");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产者生产了一个包子");
//集合中已经有元素了 唤醒获取元素的线程
list.notify();
}
}
}
}
package com;
import java.util.List;
public class Customer extends Thread{
private List<String> list;
public Customer(List<String> list){
this.list = list;
}
@Override
public void run() {
while(true){
synchronized (list){
if(list.size() == 0){
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("顾客吃了一个包子");
list.remove(0);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.notify();
}
}
}
}
import com.BunStore;
import com.Customer;
import java.util.ArrayList;
import java.util.List;
public class EatBunTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
BunStore bunStore = new BunStore(list);
Customer customer = new Customer(list);
bunStore.start();
customer.start();
}
}
5、线程同步
当多个线程共享同一资源时,可能会造成线程安全问题,因此,Java提供了一个关键字synchronized。该关键字能够修饰方 法,被称为同步方法,如果对某一段代码进行修饰和包围,代表是一个同步代码块。由于采用同步机制,所以线程会造成排队的情 况,导致程序性能下降。因此只能是在一些带有“伤害性”的代码中使用。
1、同步方法
public synchronized 返回值 方法名(参数) {
}
public class SellTicket implements Runnable{
private int ticket = 100;
@Override
public void run() {
sellTicket();
}
public synchronized void sellTicket(){
while (true){
//synchronized(this){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + "卖完了" + ticket-- + "票");
}else{
System.out.println("票卖完了!");
break;
}
//}
}
}
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
Thread thread = new Thread(sellTicket);
Thread thread1 = new Thread(sellTicket);
Thread thread2 = new Thread(sellTicket);
thread.start();
thread1.start();
thread2.start();
}
}
2、同步代码块
public void show() {
synchronized(锁对象) {
}
}
public class SellTicket implements Runnable{
private int ticket = 100;
@Override
public void run() {
sellTicket();
}
public void sellTicket(){
while (true){
synchronized(this){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + "卖完了" + ticket-- + "票");
}else{
System.out.println("票卖完了!");
break;
}
}
}
}
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
Thread thread = new Thread(sellTicket);
Thread thread1 = new Thread(sellTicket);
Thread thread2 = new Thread(sellTicket);
thread.start();
thread1.start();
thread2.start();
}
}
通过锁对象可以将当前的代码段进行锁定,其他的线程需 要排队。作为同步方法一旦被整体修饰,就代表着整个方法内部都 不能够实现异步访问。 作为同步代码块,可以先调用synchronized修饰之前的代 码,提升程序的执行性能。
3、静态同步代码块
public static synchronized 方法名() {
}
public static void show() {
synchronized(当前类名的.class) {
}
}
当在一个静态的方法上添加synchronized关键字,会采用 当前类的.class类信息作为锁对象。 如果是一个静态代码块,可以使用静态的变量进行修饰, 但是有可能会发生在外部修改锁对象的值。建议采用类的.class 作为锁对象。
线程互斥:线程带有锁后,可能会出现互斥现象。静态的 同步方法或方法块,不会出现互斥的现象。
6、LOCK锁
由于synchronized灵 活度较低,没有办法实现锁的监控过程(lock 、unlock)以及公平锁 的实现,所以在JDK5的版本中,在java.util.concurrent.locks包中 添加Lock接口,同时给了一些实现类。
package com.home;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SellTicket implements Runnable{
private Lock lock = new ReentrantLock(true);
private int ticket = 100;
@Override
public void run() {
sellTicket();
}
public void sellTicket(){
while (true){
lock.lock();
//synchronized(this){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + "卖完了" + ticket-- + "票");
lock.unlock();
}else{
System.out.println("票卖完了!");
lock.unlock();
break;
}
//}
}
}
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
Thread thread = new Thread(sellTicket);
Thread thread1 = new Thread(sellTicket);
Thread thread2 = new Thread(sellTicket);
thread.start();
thread1.start();
thread2.start();
}
}
1、synchronized与lock的区别
synchronized是Java内置的一个线程同步关键字, 而Lock是J.U.C包下面的一个接口,它有很多实现类,比如 ReentrantLock就是它的一个实现类。 synchronized可以写在需要同步的对象、方法或者是特定代的 码块中。 Lock控制锁的粒度是通过lock() 和 unlock() 方法来实现的。
Lock比synchronized在使用上相对来说要更加灵活一些。Lock 可以自主地去决定什么时候加锁,什么时候释放锁。只需要调用 lock()和unlock()这两个方法就可以了。需要注意的是,为了避免死 锁,一般我们unlock()方法写在finally块中。 另外,Lock还提供了非阻塞的竞争锁的方法叫trylock(),这个 方法可以通过返回true或者fasle来告诉当前线程是否已经有其他线 程正在使用锁。 而synchronized是关键字,无法去扩展实现非阻塞竞争锁的方 法。另外,synchronized只有代码块执行结束或者代码出现异常的 时候才会释放锁,因此,它对锁的释放是被动的。 synchronized和Lock在性能上差别不大。在实现上有一些区 别, synchronized 采用的是悲观锁机制,synchronized 是托管给 JVM 执行的。在JDK1.6以后采用了偏向锁、轻量级锁、重量级锁及 锁升级的方式进行优化。 而 Lock 用的是乐观锁机制。控制锁的代码由用于自定义,也采 用CAS自旋锁进行了优化。 二者在一般情况下没有什么区别,但是在非常复杂的同步应用 中,建议使用Lock。 因为synchronized只提供了非公平锁的实现,而Lock提供了公 平所和非公平锁的机制。 公平锁是指线程竞争锁资源的时候,如果已经有其他线程正在 排队或者等待锁释放,那么当前竞争锁的线程是无法去插队的。 而非公平锁就是不管是否线程再排队等待锁,它都会去尝试竞 争一次锁。
2、死锁
1、什么是死锁
在多线程程序中,使用了多把锁,造成线程之间相互等待.程序不往下走了。
2、产生死锁的条件
1、有多把锁 2、有多个线程 3、有同步代码嵌套
3、死锁实例
package Thread;
public class MyLock implements Runnable{
Object obj1 = new Object();
Object obj2 = new Object();
@Override
public void run() {
synchronized (obj1) {
System.out.println("嵌套1 obj1");
synchronized (obj2) {// t2, obj1, 拿不到2锁,等待
System.out.println("嵌套1 obj2");
}
}
synchronized (obj2) {
System.out.println("嵌套2 obj2");
synchronized (obj1) {// t1 , obj2, 拿不到1锁,等待
System.out.println("嵌套2 obj1");
}
}
}
}
package Thread;
public class Test3 {
public static void main(String[] args) {
MyLock ml = new MyLock();
new Thread(ml).start();
new Thread(ml).start();
}
}
7、并发包
1、ConcurrentHashMap
public class Const {
public static ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>() ;
// public static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>() ;
public static ArrayList<String> list = new ArrayList<>() ;
}
public class ConstHashMap extends Thread {
@Override
public void run() {
Map<String, String> map = Collections.synchronizedMap(Const.map);
for (int i = 0; i < 500000; i++) {
map.put(this.getName() + (i+1) ,this.getName() + i + 1) ;
}
System.out.println("线程运行结束");
}
}
public class TestHashMap {
public static void main(String[] args) {
ConstHashMap ch1 = new ConstHashMap() ;
ConstHashMap ch2 = new ConstHashMap() ;
ch1.start();
ch2.start();
try {
Thread.sleep(5*1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("================" + Const.map.size());
}
}
为什么要使用ConcurrentHashMap: 1. HashMap线程不安全,会导致数据错乱 2. 使用线程安全的Hashtable效率低下
2、CountDownLatch
CountDownLatch允许一个或多个线程等待其他线程完成操作,再执行自己。
public class PrintAC extends Thread{
private CountDownLatch cdl ;
public PrintAC(CountDownLatch cdl) {
this.cdl = cdl ;
}
@Override
public void run() {
System.out.println("A");
try {
this.cdl.await(); //等待机制
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("D");
}
}
public class PrintB extends Thread{
private CountDownLatch cdl ;
public PrintB(CountDownLatch cdl) {
this.cdl = cdl ;
}
@Override
public void run() {
System.out.println("B");
this.cdl.countDown();
System.out.println("C");
this.cdl.countDown();
}
}
public class TestCountDownLatch {
public static void main(String[] args) {
CountDownLatch cdl = new CountDownLatch(2) ;
PrintAC printAC = new PrintAC(cdl) ;
PrintB printB = new PrintB(cdl) ;
printAC.start();
try {
Thread.sleep(0);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
printB.start();
}
}
3、CyclicBarrier
CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一 个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线 程才会继续运行。
public class BossThread extends Thread{
@Override
public void run() {
System.out.println("公司人太多了,需要开除几个员工");
}
}
public class PersonThread extends Thread{
private CyclicBarrier cyclicBarrier ;
public PersonThread(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier ;
}
@Override
public void run() {
System.out.println("我们在等待老大的到来...");
try {
Thread.sleep(5000);
System.out.println("老大要到了,我得提前准备准备");
cyclicBarrier.await() ;
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
System.gc();
}
}
public class TestMeeting {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(5,new BossThread()) ;
PersonThread p1 = new PersonThread(cyclicBarrier) ;
PersonThread p2 = new PersonThread(cyclicBarrier) ;
PersonThread p3 = new PersonThread(cyclicBarrier) ;
PersonThread p4 = new PersonThread(cyclicBarrier) ;
PersonThread p5 = new PersonThread(cyclicBarrier) ;
p1.start();
p2.start();
p3.start();
p4.start();
p5.start();
}
}
4、Semaphore
Semaphore(发信号)的主要作用是控制线程的并发数量。 synchronized可以起到"锁"的作用,但某个时间段内,只能有一个线程允许执行。 Semaphore可以设置同时允许几个线程执行。 Semaphore字面意思是信号量的意思,它的作用是控制访问特定资源的线程数目。
public class ServiceImpl extends Thread{
private Service service ;
public ServiceImpl(Service service) {
this.service = service ;
}
@Override
public void run() {
service.service();
}
}
public class Service {
//fair:是否以公平锁形式对线程放行
private Semaphore semaphore = new Semaphore(30000,false) ;
public void service() {
try {
semaphore.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "该方法是一个对外服务的方法..");
System.out.println("方法执行完成了...");
semaphore.release();
}
}
public class TestService {
public static void main(String[] args) {
Service service = new Service() ;
for (int i = 0; i < 500000; i++) {
new ServiceImpl(service).start();
}
}
}
5、Exchanger
Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。 这两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程 也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据 传递给对方。
public class ThreadA extends Thread {
private Exchanger<String> exchanger;
public ThreadA(Exchanger<String> exchanger) {
super();
this.exchanger = exchanger;
}
@Override
public void run() {
try {
System.out.println("线程A欲传递值'礼物A'给线程B,并等待线程B的值...");
System.out.println("在线程A中得到线程B的值=" + exchanger.exchange("礼物A"));
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class ThreadB extends Thread {
private Exchanger<String> exchanger;
public ThreadB(Exchanger<String> exchanger) {
super();
this.exchanger = exchanger;
}
@Override
public void run() {
try {
System.out.println("线程B欲传递值'礼物B'给线程A,并等待线程A的值...");
System.out.println("在线程B中得到线程A的值=" + exchanger.exchange("礼物B"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Demo {
public static void main(String[] args) throws InterruptedException {
Exchanger<String> exchanger = new Exchanger<String>();
ThreadA a = new ThreadA(exchanger);
ThreadB b = new ThreadB(exchanger);
a.start();
b.start();
}
}