您的位置:首页 > 编程语言 > Java开发

Java线程

2018-02-01 09:52 78 查看

参考资料网站:http://ifeve.com/java-concurrency-thread-directory/

1、Java实现线程的两种方式:

1、继承Thread 通过重写run()方法 启动方法:ThreadTest test = new ThreadTest(); test.start();

2、实现Runable通过实现run()方法
启动方法Thread thread = new Thread(new ThreadTest2(),"new Thread Name"); thread.start();

个人觉得优先使用实现Runable,因为线程池可以有效的管理实现了Runnable接口的线程,如果线程池满了,

新的线程就会排队等候执行,直到线程池空闲出来为止。而如果线程是通过实现Thread子类实现的,这将会复杂一些。

注意点:启动线程的方式是start(),不是run()方法,run()方法虽然也能调用并实现相同的功能,但是,事实上,run()方法并非是由刚创建的新线程

所执行的,而是被创建新线程的当前线程所执行了。也就是被执行上面两行代码的线程所执行的。想要让创建的新线程执行run()方法,必须调用新线程

的start方法。

2、线程的内存模型

线程将基本类型存在栈内存中,把对象存在堆内存中。一个线程仅能访问自己的线程栈。一个线程创建的本地变量对其它线程不可见,
仅自己可见。即使两个线程执行同样的代码,这两个线程任然在在自己的线程栈中的代码来创建本地变量。因此,每个线程拥有每个本地变量的
独有版本。

静态成员变量跟随着类定义一起也存放在堆上。

存放在堆上的对象可以被所有持有对这个对象引用的线程访问。当一个线程可以访问一个对象时,它也可以访问这个对象的成员变量。

如果两个线程同时调用同一个对象上的同一个方法,它们将会都访问这个对象的成员变量,但是每一个线程都拥有这个本地变量的私有拷贝。


3、Java同步块synchronzied

静态和实例有区别:实例作用在方法上this,静态作用在对象上ClassName.class
下面两个例子都同步他们所调用的实例对象上,因此他们在同步的执行效果上是等效的。

public class MyClass {
public synchronized void log1(String msg1, String msg2){
log.writeln(msg1);
log.writeln(msg2);
}

public void log2(String msg1, String msg2){
synchronized(this){
log.writeln(msg1);
log.writeln(msg2);
}
}
}

public class MyClass {
public static synchronized void log1(String msg1, String msg2){
log.writeln(msg1);
log.writeln(msg2);
}
public static void log2(String msg1, String msg2){
synchronized(MyClass.class){
log.writeln(msg1);
log.writeln(msg2);
}
}
}
如果两个线程引用了两个不同的Counter实例,那么他们可以同时调用add()方法。这些方法调用了不同的对象,
因此这些方法也就同步在不同的对象上。这些方法调用将不会被阻塞。如下面这个例子所示:
public class Example {
public static void main(String[] args){
Counter counterA = new Counter();
Counter counterB = new Counter();
Thread  threadA = new CounterThread(counterA);
Thread  threadB = new CounterThread(counterB);

threadA.start();
threadB.start();
}
}

4、线程通信

关键词:wait(),notify()和notifyAll()
Java有一个内建的等待机制来允许线程在等待信号的时候变为非运行状态。
下面是对同一个对象wait()和notify()的一个案例:
public class MonitorObject {
}
public class MyWaitNotify {

MonitorObject monitorObject = new MonitorObject();
public void doWait() {
synchronized (monitorObject) {
try {
monitorObject.wait();
} catch (Exception e) {
// TODO: handle exception
}
}
}

public void doNotify() {
synchronized (monitorObject) {
monitorObject.notify();
}
}

}
如你所见,不管是等待线程还是唤醒线程都在同步块里调用wait()和notify()。这是强制性的!一个线程如果没有持有对象锁,将不能调用wait(),
notify()或者notifyAll()。否则,会抛出IllegalMonitorStateException异常。(校注:这个将被唤醒的线程是随机的,不可以指定唤醒哪个线程)

但是,这怎么可能?等待线程在同步块里面执行的时候,不是一直持有监视器对象(myMonitor对象)的锁吗?等待线程不能阻塞唤醒线程进入doNotify()的同步块吗?答案是:的确不能。一旦线程调用了wait()方法,它就释放了所持有的监视器对象上的锁。这将允许其他线程也可以调用wait()或者notify()。

如果一个线程先于被通知线程调用wait()前调用了notify(),等待的线程将错过这个信号。这可能使等待线程永远在等待,不再醒来,因为线程错过了唤醒信号。
为了避免丢失信号,必须把它们保存在信号类里。在MyWaitNotify的例子中,通知信号应被存储在MyWaitNotify实例的一个成员变量里。以下是MyWaitNotify的修改版本:调用wait()时加个判断条件,如果调用过notify()则不在调用

public class MyWaitNotify2 {

MonitorObject myMonitorObject = new MonitorObject();
boolean wasSignalled = false;

public void doWait() {
synchronized (myMonitorObject) {
if (!wasSignalled) {
try {
myMonitorObject.wait();             
 
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// clear signal and continue running.
wasSignalled = false;
}
}

public void doNotify() {
synchronized (myMonitorObject) {
wasSignalled = true;
myMonitorObject.notify();
}
}

}

5、死锁

1线程锁住A想成访问B资源,2线程同时锁住B像访问A资源,两个资源都不释放,进入无线等待中,就是相互阻塞状态,这就是死锁。
死锁出现的场景是两个线程以及两个线程以上。


数据库的死锁

更加复杂的死锁场景发生在数据库事务中。一个数据库事务可能由多条SQL更新请求组成。当在一个事务中更新一条记录,这条记录就会被锁住避免其他事务的更新请求,直到第一个事务结束。同一个事务中每一个更新请求都可能会锁住一些记录。当多个事务同时需要对一些相同的记录做更新操作时,就很有可能发生死锁。

因为锁发生在不同的请求中,并且对于一个事务来说不可能提前知道所有它需要的锁,因此很难检测和避免数据库事务中的死锁。  
如何避免死锁:1、加锁顺序,2、加锁时限,3、死锁检测; http://ifeve.com/deadlock-prevention/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: