Thread、Runnable
好久没看线程和锁的东西,今天回来复习一下关于线程和锁的一些相关知识,感觉这里很重要。下期准备去复习Connection集合。直接进入主题。线程和进程声明关系,简单对线程进行一个简单概括。
说线程就先提下进程,进程是一个内存中的运行程序,每一个进程都有一个独立的空间,一个应用层序可以有多个进程,进程是应用程序的一次执行过程,系统运行一个程序就是一个程序从创建、运行、消亡的过程。 线程就是指进程的执行单元,是系统的执行单元,一个进程可以有多个线程,至少一个线程,可以多个。直接进入主题;
线程创建的三个方法:Thread、Runnable、Callable,第三种先不说 ,先复习下前两个,继承类Thread和实现Runnable接口,重写run方法然后调用实现类,不管哪种创建线程的方式,都记得启动线程的时候是调用start()方法,不是调用run方法就行。
这里提一下之前的Runnable相对于Thread 的优势,建立线程建议使用Runnabl接口:1,适合多个相同代码使用一个共享资源,2.避免单继承局限性,3.增强代码的健壮性,实现解耦,代码可以被多个线程继承,代码和线程独立,4.线程池只能被Runnable和Callable,不能被Thread类。
Thread和Runnable这里不多解释,上次也只是说了这些,直接从Callable开始,
Callable接口’:可调用的接口
方法:
public interface Callable{
public call throws Exception;
}
与jdk1.5之后加入,与Runnable接口一昂,实现之后代表一个线程任务,Callable接口具有泛型和抛出异常
Runnable和Callable的区别
Callable声明具有泛型和抛出异常,而Runnable没有,其次Callable有一个返回值 ,而Runnable没有
案例:
package Review;
import java.util.concurrent.Callable;
public class call {
public static void main(String[] args) {
Callable<String> stringCallable = new Callable<String>() {
@Override
public String call() throws Exception {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+".........."+i);
}
return "callable";
}
};
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"..........."+i);
}
}
}).start();
try {
stringCallable.call();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Future接口:返回将要完成的结果
使用两个线程计算0-100的值,第一个线程计算0-50,第二个线程计算21-100;
package Review;
import java.util.concurrent.*;
public class callFuture {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
Future<Integer> future1 = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("计算机0-50开始");
int sum = 0;
for (int i = 0; i < 50; i++) {
sum+=i;
}
System.out.println("计算机0-50结束");
return sum;
}
});
Future<Integer> future2 = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("计算机51-100结束");
int sum = 0;
for (int i = 51; i <= 100; i++) {
sum+=i;
}
System.out.println("计算机51-100结束");
return sum;
}
});
try {
future1.get();
future2.get();
System.out.println(future1.get()+future2.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
}
线程状态
new状态:线程创建完成,通过start()进入Ready就绪状态,所有工作准备完毕,os系统选中,进入running运行状态,线程中run()执行结束,线程完成执行。在运行状态时,当有线程加入join()时,会停止该线程的执行,直接去执行加入线程的,当前线程进入无限制等待Waiting状态,只有加入线程执行结束,当前线程才会继续执行。通过sleep会使线程进入睡眠状态,限期等待TImed Waiting状态,睡眠时间到,线程继续执行。如果对线程进行了加锁操作,就是synchronized,线程就会进入阻塞状态,当获得锁之后,线程又会继续执行,最终Terminated终止状态,
1.加入线程join,会阻塞当前线程,等待加入线程运行结束,当前线程才会继续运行,
案例:
package Review;
public class addJoin {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 30; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"......"+i);
}
}
};
Thread thread1 = new Thread(runnable, "tt");
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 30; i < 69; i++) {
System.out.println(Thread.currentThread().getName()+"......"+i);
}
thread1.start();
}
}
线程睡眠
package Review;
import java.util.concurrent.*;
public class sleep {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(1);
Future<Integer> future = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 20; i++) {
sum+=i;
Thread.sleep(200);
}
return sum;
}
});
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
线程优先级
setPriorityL线程优先级,使用方法:线程点setpriority,线程优先级分别为1-10,默认为5.当优先级最高时,cpu会优先执行。
package Review;
package Review;
public class setPriority extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"..."+i);
}
}
public static void main(String[] args) {
setPriority priority = new setPriority();
Thread thread1 = new Thread(priority, "1");
Thread thread2 = new Thread(priority, "2");
thread1.setPriority(5);
thread2.setPriority(10);
thread1.start();
thread2.start();
}
}
守护线程
使用方法:线程名称.setDaemon(true) 将线程设置为守护线程,线程有两类:用户(前端线程),后台(守护线程)如果所有前端线程执行完毕,那么后台线程自动结束,守护线程是为前端线程保驾护航的。
线程放弃
当前线程主动放弃时间片,进入就绪状态,竞争下一次时间片。
package Review;
public class yield extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"..."+i);
Thread.yield();
}
}
public static void main(String[] args) {
yield yield = new yield();
Thread thread1 = new Thread(yield, "1");
Thread thread2 = new Thread(yield, "2");
thread1.start();
thread2.start();
}
}
线程通信
等待方法:
public final void wait()
public final void wait(long timeout):
必须在对obj加锁的同步代码块中,在一个线程中调用obj.wait时,此线程会释放其拥有的标记,哦同时线程阻塞在等待队列中,释放锁,进入等地啊队列
通知方法:
public final notify()
public final viod notfiy()
synchronized
同步方法:synchronized 返回值 方法名称 参数列表(){ //当前对象进行加锁
//加锁代码}
只有拥有对象互斥标记的线程,才能够进入该加锁的对象,线程在执行完之后,就会释放锁标记,给下一个线程。
案例:模拟3个窗口同时卖一百张票
方法一:同步代码块
package Review;
public class ticket implements Runnable{
private static int tic = 100;
@Override
public void run() {
while (true){
synchronized (this){
if (tic < 0){
break;
}
System.out.println(Thread.currentThread().getName()+"卖了第"+tic+"票");
tic--;
}
}
}
public static void main(String[] args) {
ticket ticket = new ticket();
new Thread(ticket,"1").start();
new Thread(ticket,"2").start();
new Thread(ticket,"3").start();
}
}
方法二:同步方法
案例:模拟3个窗口同时卖一百张票
package Review;
public class ticketSynchronized extends Thread{
private static int ticket = 100;
@Override
public void run() {
while (true){
if (!method()){
break;
}
}
}
public synchronized static boolean method(){
if (ticket <= 0){
return false;
}
System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"票");
ticket--;
return true;
}
public static void main(String[] args) {
ticketSynchronized ticket = new ticketSynchronized();
new Thread(ticket,"1").start();
new Thread(ticket,"2").start();
new Thread(ticket,"3").start();
}
}
方法三:锁 Lock
package Review;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Locks extends Thread{
private static int ticket = 100;
Lock lock = new ReentrantLock();
@Override
public void run() {
lock.lock();
while (true){
if (ticket <= 0){
break;
}
System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"票");
ticket--;
}
lock.unlock();
}
public static void main(String[] args) {
Locks locks = new Locks();
new Thread(locks,"1").start();
new Thread(locks,"2").start();
new Thread(locks,"3").start();
}
}
Lock
lock锁:是和synchronized具有相同功能且比它结构灵活、实用性更大、功能更加完善、性能更优越的锁,
方法:
void lock():获得锁,执行若被线程占用,则等待
boolean tryLock:尝试获得锁,不会造成线程阻塞,成功返回true,失败返回false
void unlock():释放锁,
重入锁:ReentranLock
一个可以重入互斥Lock且具有和synchronized相同功能并可以扩展的锁,ReentrantLock:用Lock接口实现,与synchronized一样具有互斥功能
案例使用ReentrantLock对数组增加两个元素
package Review;
import java.util.Arrays;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Reentrant {
Lock lock = new ReentrantLock();
private String[] str = {“A”,“B”,"","",""};
private int index = 0;
public void add(String value){
lock.lock();
str[index] = value;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
index++;
System.out.println(Thread.currentThread().getName());
lock.unlock();
}
public String[] get(){
return str;
}
public static void main(String[] args) {
Reentrant reentrant = new Reentrant();
Runnable runnableA = new Runnable() {
@Override
public void run() {
System.out.println("a添加");
reentrant.add("666");
}
};
Runnable runnableB = new Runnable() {
@Override
public void run() {
System.out.println("b添加");
reentrant.add("777");
}
};
Thread threadA = new Thread(runnableA, "A");
Thread threadB = new Thread(runnableB, "B");
threadA.start();
threadB.start();
try {
threadA.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
threadB.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Arrays.toString(reentrant.get()));
}
}
读写锁ReentrantReadWriteLock
读写锁支持一写多读,读写分离,可以分别分配读写锁,可以支持多次分配读写锁,使得多个线程并发执行,
读锁不会互斥,写锁会,写不能读,都不能写,两个线程都在读不会互斥,
规则:写写互斥,阻塞;读写互斥,读阻塞写,写阻塞读,读读不会互斥
案例:synchronized和ReenTrantReadWriteLock读写时间对比,创建20个线程,18个读线程,2个写线程,进行比较
读写锁案例:
package Review;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class TestRW {
ReentrantReadWriteLock locks = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock read = locks.readLock();
ReentrantReadWriteLock.WriteLock write = locks.writeLock();
String value;
public String getValue(){
read.lock();
try {
Thread.sleep(1000);
System.out.println(“写入”+value);
return this.value;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
read.unlock();
}
return this.value;
}
public String setValue(String value){
write.lock();
try {
Thread.sleep(1000);
System.out.println(“写入”+value);
return this.value;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
write.unlock();
}
return this.value;
}
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(20);
TestRW rw = new TestRW();
Runnable r = new Runnable() {
@Override
public void run() {
rw.getValue();
}
};
Runnable w = new Runnable() {
@Override
public void run() {
rw.setValue(“ee”+new Random().nextInt(100));
}
};
long start = System.currentTimeMillis();
for (int i = 0; i < 2; i++) {
service.submit(w);
}
for (int i = 0; i < 18; i++) {
service.submit(r);
}
long end = System.currentTimeMillis();
service.shutdown();
while (!service.isTerminated()){ //线程空转,保证所有线程执行完成
}
System.out.println("消耗时间:"+(end-start));
}
}
互斥锁案例:
package Review;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
public class reentrantRW {
ReentrantLock lock = new ReentrantLock(); //互斥锁
String value;
public String getValue(){
lock.lock();
try {
Thread.sleep(1000);
System.out.println("读入"+value);
return this.value;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return value;
}
public String setValue(String value){
lock.lock();
try {
System.out.println("写入"+value);
return this.value;
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
reentrantRW rw = new reentrantRW();
ExecutorService service = Executors.newFixedThreadPool(20);
Runnable write = new Runnable() {
@Override
public void run() {
rw.setValue("ee:"+ new Random().nextInt(100));
}
};
Runnable read = new Runnable() {
@Override
public void run() {
rw.getValue();
}
};
long start = System.currentTimeMillis();
for (int i = 0; i < 2; i++) {
service.submit(write);
}
for (int i = 0; i < 18; i++) {
service.submit(read);
}
service.shutdown();
while (!service.isTerminated()){ //线程空转,保证所有线程都执行完毕
}
long end = System.currentTimeMillis();
System.out.println(end-start);
}
}
为了方便 全部写在一起,建议分开写。两个案例最后都是输出了读取和输入,但是读写锁只需要3秒左右,而互斥锁则需要18秒左右,而互斥锁还可能存在阻塞清空;