P150
1.线程和进程的联系和区别:
一个进程就是一个执行中的程序。程序执行中的单个顺序的流控制叫线程。
同类的多个线程共享一块内存空间和一组系统 资源,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序 执行时使用的堆栈。所以系统在产生一个线程,或者在各个线程之间切换时, 负担要比进程小的多 。
2.前台线程和后台线程:
程序中必须要执行完所有的前台线程才可以退出,而应用程序可以不用考虑是否运行完毕后台线程而直接退出。主函数就是一个前台线程,随着thread对象生成。
3.创建线程的方法:
(1)通过继承Thread类来创建线程
public class test1 extends Thread{
public static void main(String[] args) {
Thread a=new test1();
a.start();
System.out.println("main thread");
}
public void run() {
System.out.println("another thread");
}
}
(2)利用runnable接口产生线程:
public class test2 implements Runnable{
public static void main(String[] args) {
// TODO 自动生成的方法存根
Thread a=new Thread(new test2());
a.start();
System.out.println("main thread");
}
public void run() {
System.out.println("another thread");
}
}
第一种无法继承其他类,而直接继承Thread类;第二种更加灵活,可以多个线程共享某个对象的资源。
4.线程的生命周期:
创建——可运行——运行——阻塞/死亡
可运行状态:start方法;运行状态:sleep方法;阻塞状态:wait、join方法。
5.线程安全:
比如多个线程想要对某个对象进行并发更新操作,竞争共享资源,可能会产生不正确的结果;考虑三种情形:1.数据单线程内可见;2.只读对象;3.线程安全类;
如果并发更新操作不属于上述三类,词汇需要考虑线程安全问题,在代码中安全同步机制。
解决方法:(1)修改线程模型。即不在线程之间共享该状态变量。这个方法改动较大。
(2)将对象变为不可变对象。
(3)在访问状态变量时使用同步。常用的有synchronized和lock。就是在修改或访问可变状态变量时上锁,独占变量,让此时其余线程无法访问该变量。
6.线程同步通信:
由于同一个进程内多个县城共享该进程的资源,因此对于多线程而言,一般的通信方式为使用全局变量进行通讯,主要问题即为多个县城同时对于共享区域进行读写时出现的同步问题。
实现:使用wait和notify/notifyAll的方法。
7.死锁:是指两个或以上线程在执行过程中,因为争夺资源而造成的互相等待的现象,若无外力干涉则都无法继续推进。
8.
package test;
class Resource implements Runnable{
volatile public int i;
volatile public Integer it;
public Resource(int _i) {
i=_i;
it=new Integer(i);
}
public void run() {
while(true) {
synchronized(it) {
if(i>0) {
try {
Thread.sleep(200);
}
catch (Exception e) {
}
i--;
System.out.println(Thread.currentThread().getName()+" "+i);
}
else {
System.out.println(Thread.currentThread().getName());
break;
}
}
}
}
}
public class test2{
public static void main(String[] args) {
Resource r=new Resource(8);
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t1.start();
t2.start();
}
}
t1和t2同时运行,对变量r的i属性进行自减,直到小于0;
运行结果:
Thread-0 7
Thread-0 6
Thread-0 5
Thread-0 4
Thread-0 3
Thread-0 2
Thread-0 1
Thread-1 0
Thread-0
Thread-1
9.
package test;
public class d9t{
private volatile boolean flg=false;
public synchronized void classA() {
try {
System.out.println("ThreadA is running");
if(flg==true) {
System.out.println("thread stopped");
Thread.interrupted();
}
}
catch(Exception e) {}
}
private synchronized void classB() {
try {
System.out.println("ThreadB is running");
Thread.sleep(10000);
flg=true;
}
catch(Exception e) {}
}
public static void main(String[] args) {
d9t a=new d9t();
new Thread(a::classA).start();
new Thread(a::classB).start();
}
好像不太对啊好像不是A让B停下来的
补充题:
1.FutueTask类
一个可取消的异步计算。FutureTask提供了对Future的基本实现,可以调用方法去开始和取消一个计算,可以查询计算是否完成并且获取计算结果。只有当计算完成时才能获取到计算结果,一旦计算完成,计算将不能被重启或者被取消,除非调用runAndReset方法。
除了实现了Future接口以外,FutureTask还实现了Runnable接口,因此FutureTask交由Executor执行,也可以直接用线程调用执行(futureTask.run())。根据FutureTask的run方法执行的时机,FutureTask可以处于以下三种执行状态:
1、未启动:在FutureTask.run()还没执行之前,FutureTask处于未启动状态。当创建一个FutureTask对象,并且run()方法未执行之前,FutureTask处于未启动状态。
2、已启动:FutureTask对象的run方法启动并执行的过程中,FutureTask处于已启动状态。
3、已完成:FutureTask正常执行结束,或者FutureTask执行被取消(FutureTask对象cancel方法),或者FutureTask对象run方法执行抛出异常而导致中断而结束,FutureTask都处于已完成状态。
callable和runnable都是两者都是接口,两者都需要调用Thread.start启动线程。
callable的核心是call方法,允许返回值,runnable的核心是run方法,没有返回值;call方法可以抛出异常,但是run方法不行;callable和runnable都可以应用于executors。而thread类只支持runnable。
多线程实际操作:
package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class myRunnable implements Runnable{
public void run() {
System.out.println("start this thread,last for about 1 second.");
try {
Thread.sleep(1000);
}
catch(Exception e) {
e.printStackTrace();
}
System.out.println("here comes thread number:"+Thread.currentThread().getName());
System.out.println("this ends...");
}
}
public class xianchengchi {
public static void main(String[] args) {
// TODO 自动生成的方法存根
ExecutorService dxc=Executors.newFixedThreadPool(4);
myRunnable mr=new myRunnable();
dxc.submit(mr);
dxc.submit(mr);
dxc.submit(mr);
dxc.submit(mr);
}
}
3.volatile关键字:
volatile是Java提供的一种轻量级的同步机制。
相比于synchronized(synchronized通常称为重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度。但是volatile 变量的同步性较差(有时它更简单并且开销更低),而且其使用也更容易出错。
4.volatile、synchronized;
5.泡茶模拟:
package test;
class myrunnable1 implements Runnable{
public void run() {
System.out.println("开始烧水:");
try {
Thread.sleep(10000);
}
catch(Exception e) {
e.printStackTrace();
}
System.out.println("烧好水了");
}
}
class myrunnable2 implements Runnable{
public void run() {
int i=1;
System.out.println("开始洗杯子");
for(;i<=5;i++) {
System.out.println("洗第"+i+"个杯子。。");
try {
Thread.sleep(1000);
}
catch(Exception e) {
e.printStackTrace();
}
}
System.out.println("杯子洗完了");
}
}
class myrunnable3 implements Runnable{
public void run() {
int i=1;
System.out.println("开始洗水壶");
try {
Thread.sleep(5000);
}
catch(Exception e) {
e.printStackTrace();
}
System.out.println("水壶洗好了");
}
}
public class shaoshui {
public static void main(String[] args) {
// TODO 自动生成的方法存根
myrunnable1 mr1=new myrunnable1();
myrunnable2 mr2=new myrunnable2();
myrunnable3 mr3=new myrunnable3();
Thread t1=new Thread(mr1);
Thread t2=new Thread(mr2);
Thread t3=new Thread(mr3);
t1.setName("烧水");
t2.setName("洗杯子");
t3.setName("洗水壶");
t1.start();//先烧水
t2.start();//烧水的时候洗杯子
try{
t2.join();//先洗杯子再洗水壶
}
catch(Exception e) {
e.printStackTrace();
}
t3.start();//先洗水壶再泡茶
try{
t3.join();
}
catch(Exception e) {
e.printStackTrace();
}
System.out.println("开始泡茶");
}
}
6.
package test;
class Account{
volatile private int value;
volatile private boolean ismoney=false;
synchronized void put(int i) {
if(ismoney) {
try {
wait();
}
catch(Exception e) {
e.printStackTrace();
}
}
value=value+i;
System.out.println("存入"+i+"账户金额为:"+value);
ismoney=true;
notify();
}
synchronized int get(int i) {
if(!ismoney) {
try {wait();
}
catch(Exception e) {
e.printStackTrace();
}
if(value>i) {
value=value-i;
}
else {
i=value;
value=0;
}
System.out.println("取走:"+i+"账户金额为:"+value);
ismoney=false;
notify();
}
return i;
}
}