进程与线程:两者的粒度不同,进程由操作系统管理, 线程是在一个程序(进程)内,一个进程内可以有多个进程。
java线程的调度方法:同优先级 线程组成先进先出队列,使用时间片策略。对高优先级,使用优先调度的抢占式策略。
创建线程:线程也是一种对象,只有实现了Runnalbe接口或是继承了Thread类的类的对象才有资格成为线程。
必须重写run方法;
每个线程都有属于自己的内存空间存放自己的运行状态。
如果我们同时启动多个线程,这些线程就会在CPU上 按照启动顺序在给定的时间内轮流执行:
public class SumThread extends Thread /*implements Runnable*/
{
private int from,to,sum;
public SumThread(int from,int to)
{
this.from=from;
this.to=to;
}
public int GetSum()
{
return sum;
}
public void run() //重写run方法
{
for (int i = from; i < to; i++)
{
sum = sum+i;
}
}
}
Test
public class SumThreadTest {
public static void main(String[] args) throws Exception
{
SumThread s=new SumThread(1, 100);
s.start();
s.join(); //没有使用join(),输出结果为0,原因是main线程在s线程执行完毕前输出sum的值
System.out.println(s.GetSum());
}
}
线程的生命周期:当实例化一个Thread对象并且执行 start()之后,线程就进入就绪(Runnable)状态,进入了就绪状态,并是不 表示开始执行run方法,而是被排入一个队列中,由线程调度器从队列中取出线程来轮流执行。
匿名实现类的写法:
public class PersonTest {
public static void main(String[] args)
{
final Person p=new Person();
//匿名的实现类
Thread a=new Thread(new Runnable(){
@Override
public void run() {
while(true)
{
p.setIdAndName("B.L", "BLUECE");
}
}});
Thread b=new Thread(new Runnable(){
public void run() {
while(true)
{
p.setIdAndName("J.L", "JACKSON");
}
}});
a.start();
b.start();
}
}
线程的 优先级可以使用thread的setPriority()方法来设定线程的优先权,设定的值在1~10之间,优先级由10到1依次递减,优先值高的线程将先被执行完毕。
线程的休眠是指线程暂时处于等待的一种状态,调用Thread类的 sleep()方法可以使指定的线程处于休眠状态,线程在休眠结束后, 会重新执行未完成的任务。
终止线程不建议用stop()方法,可以自行实现一个终止线程的方法,终止线程的根本在于run方法运行完毕。
public class ThreadA extends Thread
{
private boolean flag=true;
public void setflag(boolean flag)
{
this.flag = flag;
}
@Override
public void run()
{
int i=1;
while(flag)
{
System.out.println(i+"AAAAA");
i++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class ThreadATest {
public static void main(String[] args) throws Exception {
ThreadA s = new ThreadA();
s.start();
Thread.sleep(10*1000);
s.setflag(false);
}
}
当前正在运行的线程:currentThread();
判断线程是否启动:isAlive();
线程的强制运行:join();
线程的休眠:sleep();
线程的中断:interruput();
线程的礼让:yield();
线程控制基本方法:
线程的同步:synchronized
当有两个或者两个以上的线程共享同一个数据区域进行读写的操作,该共享数据区就是一个临界区。由于多个线程的执行顺序的不同会影响临界区中变量的值或程序输出,因此对临界区的访问需要特殊处理。
public class Person
{
private String id;
private String name;
private int count=1;
//为了保持线程的安全性,将方法同步
public synchronized void setIdAndName(String id,String name)
//也可以用synchronized(this){}
{
this.id=id;
this.name=name;
if(!checkIDAndName())
{
System.err.println(count+"id和name不相符");
}
count++;
Object o=new Object();//可能导致死锁
o.hashCode();
}
public synchronized boolean checkIDAndName()
{
return id.charAt(0)==name.charAt(0);
}
}
public class PersonTest {
public static void main(String[] args)
{
final Person p=new Person();
//匿名的实现类
Thread a=new Thread(new Runnable(){
@Override
public void run() {
while(true)
{
p.setIdAndName("B.L", "BLUECE");
}
}});
Thread b=new Thread(new Runnable(){
public void run() {
while(true)
{
p.setIdAndName("J.L", "JACKSON");
}
}});
a.start();
b.start();
}
}
互斥锁:
对临界区变量的读写对同一个线程应该是一个原子操作
在Java中引入了“对象互斥锁”来实现原子操作
每一个对象有一个“互斥锁”
synchronized关键字用来获取对象的互斥锁。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问
当线程A想要访问临界区中共享变量时,它必须要获得临界区对象的“对象锁”。若获取成功,则可访问临界区;否则进入阻塞状态等待其他线程释放对象锁
synchronized
synchronized (临界区对象) {......}
同步化方法:
public synchronized void add() {......}
synchronized锁定的通常是临界区对象,对临界区共享变量的访问需在synchronized代码块中进行
synchronized代码块是串行执行的,代码块的范围越小越好,代码数量越少越好
若线程A已获得对象O的对象锁,则访问对象O其他synchronized代码块时无需再次获得对象锁
一个线程可获得多个对象的对象锁
死锁:
大部分代码并不容易发生死锁,遵循以下原则有助于规避死锁
1.只在必要的最短时间内持有锁,考虑使用同步语句块代替整个同步方法。
2.尽量编写不在同一时刻需要持有多个锁的代码,如果不可避免,则确保线程持有第二个时间尽量短