摘要
首先记录线程的基本概念,然后记录如何实现线程,接着记录多线程中的同步问题,最后记录如何实现多线程之间的通讯和停止线程。
1.基本概念
进程:正在运行的程序
线程:线程是进程的执行路径或者说是控制单元,每个进程中至少有一个线程
2.实现线程的方式
i.继承Thread类重写run方法,代码如下:
class DemoThread extends Thread{
@Override
public void run() {
//线程要执行的代码放在此处
}
}
启动线程代码:
DemoThread dt = new DemoThread();
dt.start();
ii.实现Runnable接口,代码如下:
class DemoRunnable implements Runnable{
@Override
public void run() {
//线程要执行的代码放在此处
}
启动线程依赖于Thread对象,启动方式如下:
DemoRunable dr = new DemoRunable ();
Thread t = new Thread(dr);
t.start();
iii.通过线程池实现线程,代码如下:
Executors.newSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
//线程要执行的代码放在此处
}
});
继承Thread和Runnable实现的区别:
继承Thread类则无法再继承其他类,扩展性降低,然后Thread对象本身代表一个线程无法启动多个线程
实现Runnable接口时可以继承其他类扩展性好,可以通过多Thread对象启动多个线程
3.线程安全问题(线程同步)
当多个线程对同一资源进行操作时,可能导致线程安全问题。例如经典的消费者和生产者问题。
解决线程安全的方式有如下:
i.同步代码块
将操作公共资源的代码放在同步代码块当中,代码如下:
synchronized (obj) {
//需要同步的代码
}
ii.同步方法,代码如下:
public synchronized void method(){
}
iii.使用本身具有线程安全特性的集合例如HashTable 、Vector
iv. 使用java.concurrent.lock
注意使用同步锁来保证线程安全时需要保证使用的同步锁对象是同一个对象
4.线程间通讯
在多线程之间往往需要进程之间进行通讯,例如在生产者和消费者之间需要需要在生产者生产之后才可以进行消费,只能消费一次,消费一次之后需要生产者再次生产。
实现通讯的方法有:
i.wait();//线程冻结,只有唤醒之后才再次执行
ii.nofity();//唤醒某一锁下的等待线程
iii.notifyAll();//唤醒某一锁下的全部等待线程
demo代码如下:
public class IntOut {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Person p = new Person();
Thread in = new Thread(new Input(p));
Thread out = new Thread(new Output(p));
in.start();
out.start();
}
}
class Person{
private String name;
private String sex;
private boolean flag = false;
public Person(){
name="静候";
sex="女";
}
public void set(String name,String sex){
this.name = name;
this.sex = sex;
}
public void out(){
System.out.println(this.name+" "+this.sex);
}
public boolean getFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
class Input implements Runnable{
private Person p = null;
public Input(Person p){
this.p = p;
}
@Override
public void run() {
// TODO Auto-generated method stub
int i =0;
while(true){
synchronized (p) {
if(Input.this.p.getFlag()){
try {
p.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(i%2==0){
Input.this.p.set("佳音", "女");
}else{
Input.this.p.set("静候", "男");
}
i = (i+1)%2;
p.setFlag(true);
p.notify();
}
}
}
}
class Output implements Runnable{
private Person p = null;
public Output(Person p){
this.p = p;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
synchronized (p) {
if(!p.getFlag()){
try {
p.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Output.this.p.out();
p.setFlag(false);
p.notify();
}
}
}
}
5.停止线程
以往的停止线程的方法有stop,但已经过时是不安全的,停止线程的最合适的方法时run方法执行结束,而run方法中往往是存在循环,只要控制循环条件便可以停止线程。
但是有些线程被挂起冻结,则这些线程无法访问到循环控制条件则无法停止线程,可以通过interrupt方法强制使线程进入运行状态。demo代码如下:
public class StopThread implements Runnable{
/**
* @param args
*/
private boolean flag = true;
public static void main(String[] args) {
// TODO Auto-generated method stub
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int count = 0;
while(true){
if((count++)>90){
st.setFlag();
t1.interrupt();
t2.interrupt();
break;
}else{
System.out.println("main"+count);
}
}
}
@Override
public/* synchronized */void run() {
// TODO Auto-generated method stub
while(flag){
try{
//wait();
}catch(/*Interrupted*/Exception e){
System.out.println("in exception");
flag = false;
}
System.out.println("in"+Thread.currentThread().getName());
}
}
public void setFlag(){
this.flag = false;
}
}
总结
这篇日记记录了线程的基本内容,但没有详细的记录线程间通讯的问题(多生产者多消费者的问题)和使用lock实现锁的功能,及Executor的使用,右面有其他日记描述。